Hystrix 是 Netflix 开源的一款容错框架,Ta 提供了以下几种容错方法:
我不打算就「资源隔离」这个名词直接进行解释(罗列单纯的概念还是太抽象了,不好理解)。进入正题之前,让我们先了解下,下文将出现的案例的基本情况,该案例是基于 SpringBoot + Tomcat 的一个微服务,Ta 提供了以下三个服务:
其中,createReport 一个很耗时的处理逻辑,其他两个服务 login、queryUser 都很快。Tomcat 最大线程数为 5,相关的 application.yaml 配置如下:
server:port: 8080tomcat:# 最大连接数,设置为0为不限制;如果不设置,则默认 10000max-connections: 10# 最大线程数,设置为0不限制;如果不设置,则默认 200max-threads: 5# 最小线程数,设置为0不限制;如果不设置,则默认 10min-spare-threads: 5
基本情况已经介绍完了,让我们赶紧进入正题吧,请看下图:
图中的线程1、线程2、线程3、线程4、线程5是 Tomcat 线程池中的线程。上图要表达的意思是 Tomcat 线程池当中的 5 个线程都被耗时的 createReport 请求占用,导致 login、queryUser 请求无法得到快速响应。
让我来做一个假设,如果当前 createReport 的请求量暴增到 1W+,将会发生什么呢?
login 和 queryUser 请求要和 1W+ 的 createReport 请求一起排队,等待被释放的线程。所以,最终的结果很可能就是“等到花儿也谢了”也轮不到 login 和 queryUser 这哥俩儿,发起 login 和 queryUser 请求的客户端也就一直无法得到响应了。
那我们要如何解决这个问题呢?
让我们再来看张图,如下:
上图要表达的意思是,通过对处理 createReport 请求可使用的资源数量的限制(最多只能使用线程池中的 3 个线程),保证 createReport 请求不会占用线程池中的所有线程,也就说线程池将会保证一定量(此案例中是 2 个线程)的线程不被 createReport 请求占用,用来处理其他其他业务请求(例如:login、queryUser 请求)。
让我们再做一次之前的假设,createReport 的请求量暴增到 1W+,将会发生什么呢?
1W+ 的 createReport 请求最多只能占用 Tomcat 线程池中的 3 个线程,另外 2 个线程可以处理 login 和queryUser 请求。这样,就做到了大量的 createReport 请求不会影响到发起 login 和 queryUser 请求的客户端得到响应。
Hystrix 中的 「资源隔离」 就是基于上文解决思路的具体实现方案。Hystrix 有线程池、信号量 2 个隔离策略,下面我将一一说明。
线程池是 「资源隔离」 的默认策略,如下图:
上图要表达意思是:
当 createReport-4、createReport-5 触发了 fallback 后,会发生什么呢?让我们再来看一张图,如下:
这张图要表达的是,当 createReport-4、createReport-5 触发了 fallback 后,会迅速将结果返回给客户端,同时将 Tomcat 的线程(如:线程4、线程5)释放。这样,被释放的线程就可以用来处理处于等待中的 login 和 queryUser 请求了。
配置说明:
hystrix:threadpool:default:coreSize: 5maximumSize: 5maxQueueSize: 5queueSizeRejectionThreshold: 2
配置项 | 说明 |
---|---|
coreSize | 同时能处理多少个请求 |
maximumSize | 最大能处理多少个请求,也就是当请求数超过了coreSize + maxQueueSize,则还能扩增 maximumSize - coreSize 个线程来处理多出来的请求 |
allowMaximumSizeToDivergeFromCoreSize | 该参数默认为 false,只有设置为 true,才允许 maximumSize 与 coreSize不一致 |
maxQueueSize | 默认 -1,使用 SynchronousQueue,如果是设置为一个正数则使用 LinkedBlockingQueue;当 coreSize = maximumSize,超过 coreSize 的请求,将被放入 BlockingQueue 队列中等待,maxQueueSize 为该队列的最大值;当coreSize < maximumSize 且 allowMaximumSizeToDivergeFromCoreSize = true,超过 maximumSize - coreSize 的请求,将被放入 BlockingQueue 队列中等待,maxQueueSize 为该队列的最大值 |
queueSizeRejectionThreshold | 当 maxQueueSize 设置了值后才会生效,即使 maxQueueSize 没有达到,达到 queueSizeRejectionThreshold 该值后,请求也会被拒绝,默认值 5 |
allowMaximumSizeToDivergeFromCoreSize 为 true 的两种场景:
前提条件:
将 createReport 服务设置为睡眠 60 分钟,模拟请求长时间不返回。
coreSize < maximumSize,配置如下:
coreSize: 3
maximumSize: 5
maxQueueSize: 1
allowMaximumSizeToDivergeFromCoreSize: true
结论:第1、2、3个请求都被接收处理。第4个请求接收了但没有被处理(即第4个请求进入队列中等待了),第5、6个请求同样被接收处理(即扩增了2个线程来处理这两个请求)。此时,第4个请求仍然在队列中等待。当第7、8…n个请求进来的时候直接fallback。
coreSize: 3
maximumSize: 2
maxQueueSize: 1
allowMaximumSizeToDivergeFromCoreSize: true
结论:第1、2、3个请求都被接收处理。第4个接收了但没有被处理(即第4个请求进入队列中等待了)。第5、6…n个请求进来后直接fallback。所以,maximumSize
< coreSize 不会导致报错,不会出现线程扩增,执行效果与 coreSize=maximumSize 一致。
可以简单的把信号量理解为一个计数器,每一个服务(例如:createReport 服务、login服务、queryUser服务)都有一个属于自己的计数器,它们独立存在互不影响。拿 createReport 服务为例,信号量设置为 3,当第 1 个请求被 createReport 服务接收处理,则 createReport 服务对应的计数器将 +1,以此类推,当计数器累计到 3 之后,后续的请求将被拒绝(也就是触发熔断),如下图:
上图所表达的是,createReport 服务此时正在处理 createReport-1、createReport-2、createReport-3 这3 个请求,已达到信号量设置的最大值 3。所以,后续的 createReport-4、createReport-5 请求会直接被熔断掉。熔断后所发生的事情和线程池策略一样,这里就不再啰嗦了。
配置说明:
hystrix:command:default:execution:isolation:strategy: SEMAPHOREsemaphore:maxConcurrentRequests: 3
配置项 | 说明 |
---|---|
strategy | 用来指定 Hystrix 资源隔离的策略是 THREAD(线程池)还是 SEMAPHORE(信号量),默认为 THREAD |
maxConcurrentRequests | 当 strategy 为 SEMAPHORE 时,HystrixCommand.run() 方法允许的最大请求数。如果达到最大并发数时,后续请求会被拒绝。默认值是 10 |
还是先看图,如下:
左侧是线程池策略,右侧是信号量策略。从图中我们可以看出来,线程池策略里接收请求的线程(Tomcat 线程)和实际处理业务的线程(另起一个新的线程)是分开的;而信号量,则是使用的同一个线程,即接收请求的线程和实际处理业务的线程都是同一个 Tomcat 线程。
配置项 | 说明 |
---|---|
是否启用熔断器,默认是TRUE,可以通过该参数设置为FALSE来关闭熔断功能。 | |
circuitBreaker.forceOpen | 熔断器强制打开,始终保持打开状态,不关注熔断开关的实际状态。默认值FLASE。 |
circuitBreaker.forceClosed | 熔断器强制关闭,始终保持关闭状态,不关注熔断开关的实际状态。默认值FLASE。 |
错误率,默认值50%,例如一段时间(10s)内有100个请求,其中有54个超时或者异常,那么这段时间内的错误率是54%,大于了默认值50%,这种情况下会触发熔断器打开。 | |
默认值20。含义是一段时间内至少有20个请求才进行errorThresholdPercentage计算。比如一段时间了有19个请求,且这些请求全部失败了,错误率是100%,但熔断器不会打开,总请求数不满足20。 | |
circuitBreaker.sleepWindowInMilliseconds | 半开状态试探睡眠时间,默认值5000ms。如:当熔断器开启5000ms之后,会尝试放过去一部分流量进行试探,确定依赖服务是否恢复。 |
第一步:调用 allowRequest() 判断是否允许将请求提交到线程池
第二步:调用isOpen()判断熔断器开关是否打开
第三步:调用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复
相较「熔断」、「降级」,本人对「资源隔离」花了更多心思,所以在本文中比较大的篇幅都是在说我对「资源隔离」的理解。对于「熔断」、「降级」只是简单的从其他一些文章中抄录过来的(PS:等以后有机会再梳理),如果想对这两块儿有更多的了解,可以参考下我下边提供的几个链接。
本文发布于:2024-01-29 07:30:47,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170648465013693.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |