对 Hystrix 的一些个人理解

阅读: 评论:0

对 Hystrix 的一些个人理解

对 Hystrix 的一些个人理解

一、Hystrix 简介

Hystrix 是 Netflix 开源的一款容错框架,Ta 提供了以下几种容错方法:

  • 资源隔离
  • 熔断
  • 降级

二、资源隔离

面对的问题

我不打算就「资源隔离」这个名词直接进行解释(罗列单纯的概念还是太抽象了,不好理解)。进入正题之前,让我们先了解下,下文将出现的案例的基本情况,该案例是基于 SpringBoot + Tomcat 的一个微服务,Ta 提供了以下三个服务:

  • createReport 创建报告
  • login 用户登录
  • queryUser 用户查询

其中,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 中的 「资源隔离」 就是基于上文解决思路的具体实现方案。Hystrix 有线程池、信号量 2 个隔离策略,下面我将一一说明。

策略一:线程池

线程池是 「资源隔离」 的默认策略,如下图:

上图要表达意思是:

  • 请求线程(也就是 Tomcat 线程,如线程1、线程2、线程3、线程4、线程5)和处理线程(线程A、线程B、线程C)不是一个线程。
  • 有一个独立的线程池来处理 createReport 的请求,该线程池最大线程数量是 3。
  • createReport 请求占用了 Tomcat 线程池中的所有 5 个线程。所以,login、queryUser 此时将等待线程被释放。
  • 处理 createReport 请求的线程数量已达到最大值 3。所以,后续的 createReport-4、createReport-5 将会直接触发 fallback。

当 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 > maximumSize 配置如下:
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 线程。

三、熔断

划重点
  • Hystrix 在运行过程中会向 commandkey 对应的熔断器报告成功、失败、超时和拒绝的状态,熔断器维护并统计这些数据,并根据这些统计数据来决定熔断开关是否打开
  • 熔断开关打开后,后续的请求会快速返回
  • 隔一段时间(默认5秒)后熔断器会尝试半开,放入一部分请求进来,如果请求成功,则关闭熔断器
相关配置
配置项说明
是否启用熔断器,默认是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() 判断是否允许将请求提交到线程池

  1. 如果熔断器强制打开,circuitBreaker.forceOpen为true,不允许放行,返回。
  2. 如果熔断器强制关闭,circuitBreaker.forceClosed为true,允许放行。此外不必关注熔断器实际状态,也就是说熔断器仍然会维护统计数据和开关状态,只是不生效而已。

第二步:调用isOpen()判断熔断器开关是否打开

  1. 如果熔断器开关打开,进入第三步,否则继续;
  2. 如果一个周期内总的请求数小于questVolumeThreshold的值,允许请求放行,否则继续;
  3. 如果一个周期内错误率小于ThresholdPercentage的值,允许请求放行。否则,打开熔断器开关,进入第三步。

第三步:调用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复

  1. 如果熔断器打开,且距离熔断器打开的时间或上一次试探请求放行的时间超过circuitBreaker.sleepWindowInMilliseconds的值时,熔断器器进入半开状态,允许放行一个试探请求;否则,不允许放行。

四、降级

触发降级的情况
  1. 执行 construct() 或 run() 抛出异常
  2. 熔断器打开导致命令短路
  3. 命令的线程池和队列或信号量的容量超额,命令被拒绝
  4. 命令执行超时
回退方式
  • Fail Fast 快速失败
  • Fail Silent 无声失败
  • Fallback: Static
  • Fallback: Stubbed
  • Fallback: Cache via Network
  • Primary + Secondary with Fallback

五、写在后边的话

相较「熔断」、「降级」,本人对「资源隔离」花了更多心思,所以在本文中比较大的篇幅都是在说我对「资源隔离」的理解。对于「熔断」、「降级」只是简单的从其他一些文章中抄录过来的(PS:等以后有机会再梳理),如果想对这两块儿有更多的了解,可以参考下我下边提供的几个链接。

六、参考

本文发布于:2024-01-29 07:30:47,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170648465013693.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:Hystrix
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23