diff --git a/README.md b/README.md index c9cdf48..87de3b8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ 本系列知识出自中华石杉,我对这部分知识做了一个系统的整理,方便学习查阅。By the way,微信公众号**石杉的架构笔记**(id:shishan100)有其它很多架构知识,墙裂推荐~ -一点小建议:学习本系列知识之前,如果你完全没接触过 `MQ`、`ES`、`Redis`、`Dubbo`、`Hystrix` 等,那么我建议你可以先在网上搜一下每一块知识的快速入门,跟着入门 Demo 玩一下,然后再开始每一块知识的学习,这样效果更好噢~ +一点小建议:学习本系列知识之前,如果你完全没接触过 `MQ`、`ES`、`Redis`、`Dubbo`、`Hystrix` 等,那么我建议你可以先在网上搜一下每一块知识的快速入门,跟着入门 Demo [玩](https://doocs.github.io/advanced-java/#/offer)一下,然后再开始每一块知识的学习,这样效果更好噢~ ## 高并发架构 @@ -86,6 +86,7 @@ - [Hystrix 信号量机制实现资源隔离](/docs/high-availability/hystrix-semphore-isolation.md) - [Hystrix 隔离策略细粒度控制](/docs/high-availability/hystrix-execution-isolation.md) - [深入 Hystrix 执行时内部原理](/docs/high-availability/hystrix-process.md) +- [基于 request cache 请求缓存技术优化批量商品数据查询接口](/docs/high-availability/hystrix-request-cache.md) ### 高可用系统 - 如何设计一个高可用系统? diff --git a/docs/high-availability/hystrix-process.md b/docs/high-availability/hystrix-process.md index d877607..7f7e33c 100644 --- a/docs/high-availability/hystrix-process.md +++ b/docs/high-availability/hystrix-process.md @@ -72,7 +72,7 @@ final Future delegate = toObservable().toBlocking().toFuture(); 如果这个 command 开启了请求缓存 Request Cache,而且这个调用的结果在缓存中存在,那么直接从缓存中返回结果。否则,继续往后的步骤。 ### 步骤四:检查是否开启了断路器 -检查这个 command 对应的依赖服务是否开启了断路器。如果短路器被打开了,那么 Hystrix 就不会执行这个 command,而是直接去执行 fallback 降级机制,返回降级结果。 +检查这个 command 对应的依赖服务是否开启了断路器。如果断路器被打开了,那么 Hystrix 就不会执行这个 command,而是直接去执行 fallback 降级机制,返回降级结果。 ### 步骤五:检查线程池/队列/信号量是否已满 如果这个 command 线程池和队列已满,或者 semaphore 信号量已满,那么也不会执行 command,而是直接去调用 fallback 降级机制,同时发送 reject 信息给断路器统计。 @@ -115,16 +115,16 @@ observable.subscribe(new Observer() { }); ``` -如果 HystrixCommand.run() 或者 HystrixObservableCommand.construct() 的执行时间超过了 timeout 时长的话,那么 command 所在的线程会抛出一个 TimeoutException,这时会执行 fallback 降级机制,不会去管 run() 或 construct() 返回的值了。另一种情况,如果 command 执行出错抛出了其它异常,那么也会走 fallback 降级。这两种情况下,Hystrix 都会发送异常事件给断路器统计。 +如果是采用线程池方式,并且 HystrixCommand.run() 或者 HystrixObservableCommand.construct() 的执行时间超过了 timeout 时长的话,那么 command 所在的线程会抛出一个 TimeoutException,这时会执行 fallback 降级机制,不会去管 run() 或 construct() 返回的值了。另一种情况,如果 command 执行出错抛出了其它异常,那么也会走 fallback 降级。这两种情况下,Hystrix 都会发送异常事件给断路器统计。 **注意**,我们是不可能终止掉一个调用严重延迟的依赖服务的线程的,只能说给你抛出来一个 TimeoutException。 -如果没有 timeout,也正常执行的话,那么调用线程就会拿到一些调用依赖服务获取到的结果,然后 Hystrix 也会做一些 logging 记录和 metric 统计。 +如果没有 timeout,也正常执行的话,那么调用线程就会拿到一些调用依赖服务获取到的结果,然后 Hystrix 也会做一些 logging 记录和 metric 度量统计。 ![hystrix-process](/img/hystrix-process.png) ### 步骤七:断路健康检查 -Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout 等事件发送给 circuit breaker 断路器。断路器就会对这些事件的次数进行统计,根据异常事件发生的比例来决定是否要进行断路(熔断)。如果打开了断路器,那么在一段时间内,会直接断路,返回降级结果。 +Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout 等事件发送给 circuit breaker 断路器。断路器就会对这些事件的次数进行统计,根据异常事件发生的比例来决定是否要进行断路(熔断)。如果打开了断路器,那么在接下来一段时间内,会直接断路,返回降级结果。 如果在之后,断路器尝试执行 command,调用没有出错,返回了正常结果,那么 Hystrix 就会把断路器关闭。 @@ -132,13 +132,13 @@ Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout 在以下几种情况中,Hystrix 会调用 fallback 降级机制。 - 断路器处于打开状态; -- 线程池/对列/semaphore满了; +- 线程池/队列/semaphore满了; - command 执行超时; - run() 或者 construct() 抛出异常。 一般在降级机制中,都建议给出一些默认的返回值,比如静态的一些代码逻辑,或者从内存中的缓存中提取一些数据,在这里尽量不要再进行网络请求了。 -即使在降级中,一定要进行网络调用的话,也应该将那个调用放在一个 HystrixCommand 中进行隔离。 +在降级中,如果一定要进行网络调用的话,也应该将那个调用放在一个 HystrixCommand 中进行隔离。 - HystrixCommand 中,实现 getFallback() 方法,可以提供降级机制。 - HystrixObservableCommand 中,实现 resumeWithFallback() 方法,返回一个 Observable 对象,可以提供降级结果。 diff --git a/docs/high-availability/hystrix-request-cache.md b/docs/high-availability/hystrix-request-cache.md new file mode 100644 index 0000000..089473b --- /dev/null +++ b/docs/high-availability/hystrix-request-cache.md @@ -0,0 +1,75 @@ +## 基于 request cache 请求缓存技术优化批量商品数据查询接口 +Hystrix command 执行时 8 大步骤第三步,就是检查 Request cache 是否有缓存。 + +首先,有一个概念,叫做 Request Context 请求上下文,一般来说,在一个 web 应用中,如果我们用到了 Hystrix,我们会在一个 filter 里面,对每一个请求都施加一个请求上下文。就是说,每一次请求,就是一次请求上下文。然后在这次请求上下文中,我们会去执行 N 多代码,调用 N 多依赖服务,有的依赖服务可能还会调用好几次。 + +在一次请求上下文中,如果有多个 command,参数都是一样的,调用的接口也是一样的,其实结果可以认为也是一样的。那么这个时候,我们可以让第一个 command 执行返回的结果缓存在内存中,然后这个请求上下文后续的其它对这个依赖的调用全部从内存中取出缓存结果就可以了。 + +这样的话,好处在于不用在一次请求上下文中反复多次执行一样的 command,**避免重复执行网络请求,提升整个请求的性能**。 + +举个栗子。比如说我们在一次请求上下文中,请求获取 productId 为 1 的数据,第一次缓存中没有,那么会从商品服务中获取数据,返回最新数据结果,同时将数据缓存在内存中。后续同一次请求上下文中,如果还有获取 productId 为 1 的请求,直接从缓存中取就好了。 + +![hystrix-request-cache](/img/hystrix-request-cache.png) + +HystrixCommand 和 HystrixObservableCommand 都可以指定一个缓存 key,然后 Hystrix 会自动进行缓存,接着在同一个 request context 内,再次访问的话,就会直接取用缓存。 + +下面,我们结合一个具体的**业务场景**,来看一下如何使用 request cache 请求缓存技术。 + +现在,假设我们要做一个批量查询商品数据的接口,在这个里面,我们是 HystrixObservableCommand 一次性批量查询多个商品 id 的数据。但是这里有个问题,如果说 Nginx 在本地缓存失效了,重新获取一批缓存,传递过来的 productIds 都没有进行去重,比如`productIds=1,1,1,2,2`,那么可能说,商品 id 出现了重复,如果按照我们之前的业务逻辑,可能就会重复对 productId=1 的商品查询三次,productId=2 的商品查询两次。 + +我们对批量查询商品数据的接口,可以用 request cache 做一个优化,就是说一次请求,就是一次request context,对相同的商品查询只执行一次,其余的都走 request cache。 + +### 实现 Hystrix 请求上下文过滤器,并注册到 Application。 +定义 HystrixRequestContextFilter 类,实现 Filter 接口。 + +```java +/** + * Hystrix 请求上下文过滤器 + */ +public class HystrixRequestContextFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { + HystrixRequestContext context = HystrixRequestContext.initializeContext(); + try { + filterChain.doFilter(servletRequest, servletResponse); + } catch (IOException | ServletException e) { + e.printStackTrace(); + } finally { + context.shutdown(); + } + } + + @Override + public void destroy() { + + } +} +``` + +然后将该对象注册到 Application 中。 + +```java +@SpringBootApplication +public class EshopApplication { + + public static void main(String[] args) { + SpringApplication.run(EshopApplication.class, args); + } + + @Bean + public FilterRegistrationBean filterRegistrationBean() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new HystrixRequestContextFilter()); + filterRegistrationBean.addUrlPatterns("/*"); + return filterRegistrationBean; + } +} +``` + +新年快乐...待补充...... + diff --git a/docs/high-availability/img/hystrix-request-cache.png b/docs/high-availability/img/hystrix-request-cache.png new file mode 100644 index 0000000..a75e6cd Binary files /dev/null and b/docs/high-availability/img/hystrix-request-cache.png differ diff --git a/img/hystrix-request-cache.png b/img/hystrix-request-cache.png new file mode 100644 index 0000000..a75e6cd Binary files /dev/null and b/img/hystrix-request-cache.png differ diff --git a/offer.md b/offer.md index be66189..e087806 100644 --- a/offer.md +++ b/offer.md @@ -1,4 +1,4 @@ -[![where-is-my-offer](/img/where-is-my-offer.png)](https://doocs.github.io/advanced-java) +[![where-is-my-offer](/img/where-is-my-offer.png)](https://doocs.github.io/advanced-java/#/offer)