一、什么是 Hystrix?
在分布式环境中,许多服务依赖项中的一些不可避免地会失败。Hystrix 是一个库,它通过添加延迟容错和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点、停止它们之间的级联故障并提供回退选项来做到这一点,所有这些都可以提高系统的整体弹性。
二、Hystrix 有什么用?
Hystrix 旨在执行以下操作:
- 通过第三方客户端库访问(通常通过网络)依赖关系,保护和控制延迟和故障。
- 停止复杂分布式系统中的级联故障。
- 快速失败并迅速恢复。
- 尽可能回退并优雅降级。
- 实现近乎实时的监控、警报和操作控制。
三、Hystrix 解决了什么问题?
复杂分布式架构中的应用程序有几十个依赖项,每个依赖项都不可避免地会在某些时候失败。如果主机应用程序没有与这些外部故障隔离开来,它就有被它们一起关闭的风险。
例如,对于依赖于 30 个服务且每个服务的正常运行时间为 99.99% 的应用程序,您可以期待以下内容:
99.99 30 = 99.7% 的正常运行时间 10 亿次请求的 0.3% = 3,000,000 次故障 2 小时以上的停机时
间/月,即使所有依赖项都具有出色的正常运行时间。
现实通常更糟。
即使所有依赖项都表现良好,如果您不对整个系统进行弹性设计,即使对数十项服务中的每项服务造成
0.01% 的停机时间的总影响也相当于可能每月停机数小时。
正常的应用访问链
当部分服务不可用时,可能造成雪崩
在大流量的情况下,单个后端依赖变得潜在可能会导致所有服务器上的所有资源在几秒钟内变得饱和。
应用程序中通过网络或进入可能导致网络请求的客户端库的每个点都是潜在故障的根源。比故障更糟糕
的是,这些应用程序还可能导致服务之间的延迟增加,这会备份队列、线程和其他系统资源,从而导致
整个系统出现更多的级联故障。
当通过第三方客户端执行网络访问时,这些问题会更加严重——一个“黑匣子”,其中实现细节被隐藏并
且可以随时更改,并且每个客户端库的网络或资源配置都不同,并且通常难以监控和改变。
更糟糕的是传递依赖,它们执行潜在的昂贵或容易出错的网络调用,而没有被应用程序显式调用。
网络连接失败或降级、服务和服务器出现故障或变慢、新的库或服务部署会改变行为或性能特征都会导
致客户端出现错误。
所有这些都代表了需要隔离和管理的故障和延迟,以便单个故障依赖项无法关闭整个应用程序或系统。
四、Hystrix 的设计原则是什么?
Hystrix 通过以下方式工作:
- 防止任何单个依赖项用完所有容器(例如 Tomcat)用户线程。
- 减轻负载并快速失败,而不是排队。
- 在可行的情况下提供回退,以保护用户免于失败。
- 使用隔离技术(例如隔板、泳道和断路器模式)来限制任何一种依赖项的影响。
- 通过近乎实时的指标、监控和警报优化发现时间
- 通过配置更改的低延迟传播和对 Hystrix 大部分方面的动态属性更改的支持来优化恢复时间,这允许您使用低延迟反馈循环进行实时操作修改。
- 防止在整个依赖客户端执行中出现故障,而不仅仅是在网络流量中。
五、Hystrix 如何实现其目标?
Hystrix 通过以下方式做到这一点:
- 将对外部系统(或“依赖项”)的所有调用包装在一个HystrixCommand或HystrixObservableCommand对象中,该对象通常在单独的线程中执行(这是命令模式的一个示例)。
- 超时调用时间超过您定义的阈值。有一个默认值,但是对于大多数依赖项,您可以通过“属性”自定义设置这些超时,以便它们略高于每个依赖项测量的 99.5 %性能。
- 为每个依赖项维护一个小型线程池(或信号量);如果它已满,发往该依赖项的请求将立即被拒绝而不是排队。
测量成功、失败(客户端抛出的异常)、超时和线程拒绝。 - 如果服务的错误百分比超过阈值,则手动或自动触发断路器以在一段时间内停止对特定服务的所有请求。
- 当请求失败、被拒绝、超时或短路时执行回退逻辑。
- 近乎实时地监控指标和配置更改。
当您使用 Hystrix 包装每个底层依赖项时,如上图所示的架构将更改为类似于下图。每个依赖项相互隔离,限制在发生延迟时它可以饱和的资源,并包含在回退逻辑中,该逻辑决定当依赖项中发生任何类型的故障时做出什么响应:
六、实践配置
application.yml
#FeignClientProperties
feign:
hystrix:
enabled: true
client:
config:
#JDK默认HttpURLConnection 实现的 Http Client配置
default:
#连接超时时间
connectTimeout: 5000
#读取超时时间
readTimeout: 5000
# #错误解码器
# errorDecoder: feign.codec.Default
# #解码器
# encoder: ceign.codec.Default
# #编码器
# decoder: eign.codec.Default
#HystrixCommandProperties
hystrix:
command:
#ProviderFeign#hello()
default:
requestLogEnabled: true
execution:
timeout:
#是否给方法执行设置超时时间,默认为true。一般我们不要改。
enabled: true
#线程隔离相关
isolation:
#配置请求隔离的方式,这里是默认的线程池方式。还有一种信号量的方式semaphore,使用比较少。
strategy: THREAD
thread:
#方式执行的超时时间,默认为1000毫秒,在实际场景中需要根据情况设置
timeoutInMilliseconds: 5000
#发生超时时是否中断方法的执行,默认值为true。不要改。
interruptOnTimeout: true
#是否在方法执行被取消时中断方法,默认值为false。没有实际意义,默认就好!
interruptOnCancel: false
#熔断器相关配置
circuitBreaker:
#说明:是否启动熔断器,默认为true。我们使用Hystrix的目的就是为了熔断器,不要改,否则就不要引入Hystrix。
enabled: true
#说明1:启用熔断器功能窗口时间内的最小请求数,假设我们设置的窗口时间为10秒,
#说明2:那么如果此时默认值为20的话,那么即便10秒内有19个请求都失败也不会打开熔断器。
#说明3:此配置项需要根据接口的QPS进行计算,值太小会有误打开熔断器的可能,而如果值太大超出了时间窗口内的总请求数,则熔断永远也不会被触发
#说明4:建议设置一般为:QPS*窗口描述*60%
requestVolumeThreshold: 20
#说明1:熔断器被打开后,所有的请求都会被快速失败掉,但是何时恢复服务是一个问题。熔断器打开后,Hystrix会在经过一段时间后就放行一条请求
#说明2:如果请求能够执行成功,则说明此时服务可能已经恢复了正常,那么熔断器会关闭;相反执行失败,则认为服务仍然不可用,熔断器保持打开。
#说明3:所以此配置的作用是指定熔断器打开后多长时间内允许一次请求尝试执行,官方默认配置为5秒。
sleepWindowInMilliseconds: 5000
#说明1:该配置是指在通过滑动窗口获取到当前时间段内Hystrix方法执行失败的几率后,根据此配置来判断是否需要打开熔断器
#说明2:这里官方的默认配置为50,即窗口时间内超过50%的请求失败后就会打开熔断器将后续请求快速失败掉
errorThresholdPercentage: 50
#说明:是否强制启用熔断器,默认false,没有什么场景需要这么配置,忽略!
forceOpen: false
#说明:是否强制关闭熔断器,默认false,没有什么场景需要这么配置,忽略!
forceClosed: false
#统计器
metrics:
rollingStats:
#说明:此配置用于设置Hystrix统计滑动窗口的时间,单位为毫秒,默认设置为10000毫秒,即一个滑动窗口默认统计的是10秒内的请求数据。
timeInMilliseconds: 10000
#说明2:此属性指定了滑动统计窗口划分的桶数。默认为10。
#说明2:需要注意的是,metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0必须成立,否则就会抛出异常
numBuckets: 10
rollingPercentile:
#说明1:此属性配置统计方法是否响应时间百分比,默认为true。
#说明2:Hystrix会统计方法执行1%,10%,50%,90%,99%等比例请求的平均耗时用来生成统计图表。
#说明3:如果禁用该参数设置false,那么所有汇总统计信息(平均值、百分位数)将返回-1。
enabled: true
#说明:统计响应时间百分比时的窗口大小,默认为60000毫秒,即1分钟
timeInMilliseconds: 60000
#说明1:此属性用于设置滑动百分比窗口要划分的桶数,默认为6。
#说明2:需要注意的是,metrics.rollingPercentile.timeInMilliseconds %metrics.rollingPercentile.numBuckets == 0必须成立,否则会抛出异常
numBuckets: 6
#说明1:该属性表示统计响应时间百分比,每个滑动窗口的桶内要保存的请求数,默认为100。
#说明2:即默认10秒的桶内,如果执行了500次请求,那么只有最后100次请求执行的信息会被保存到桶内。
#说明3:增加这个值会增加内存消耗量,一般情况下无需更改。
bucketSize: 100
healthSnapshot:
#说明1:该参数配置了健康数据统计器(会影响Hystrix熔断)中每个桶的大小,默认为500毫秒。
#说明2:在统计时Hystrix通过metrics.rollingStats.timeInMilliseconds metrics.healthSnapshot.intervalInMilliseconds计算出桶数。
#说明3:在窗口滑动时,每滑过一个桶的时间就统计一次当前窗口内请求的失败率。
intervalInMilliseconds: 500
threadpool:
default:
#说明:核心线程池的大小,默认值是10
coreSize: 1
#说明:是否允许线程池扩展到最大线程池数量,默认为false。
allowMaximumSizeToDivergeFromCoreSize: false
#说明:线程池中线程的最大数量,默认值是10。此配置项单独配置时并不会生效,需要启用allowMaximumSizeToDivergeFromCoreSize
maximumSize: 10
#说明1:作业队列的最大值,默认值为-1。表示队列会使用SynchronousQueue,此时值为0,Hystrix不会向队列内存放作业。
#说明2:如果此值设置为一个正int型,队列会使用一个固定size的LinkedBlockingQueue,此时在核心线程池都忙碌的情况下,会将作业暂时存放在此队列内,但是超出此队列的请求依然会被拒绝
maxQueueSize: -1
#设置队列拒绝请求的阀值,默认为5。
queueSizeRejectionThreshold: 5
#控制线程在释放前未使用的时间,默认为1分钟。
keepAliveTimeMinutes: 1
ribbon:
#说明:同一台实例的最大自动重试次数,默认为1次,不包括首次
MaxAutoRetries: 1
#说明:要重试的下一个实例的最大数量,默认为1,不包括第一次被调用的实例
MaxAutoRetriesNextServer: 1
#说明:是否所有的操作都重试,默认为true
OkToRetryOnAllOperations: true
#说明:从注册中心刷新服务器列表信息的时间间隔,默认为2000毫秒,即2秒
ServerListRefreshInterval: 2000
#说明:使用Apache HttpClient连接超时时间,单位为毫秒
ConnectTimeout: 6000
#说明:使用Apache HttpClient读取的超时时间,单位为毫秒
ReadTimeout: 6000
#说明:初始服务器列表,不需要手工配置,在运行时动态根据注册中心更新
# listOfServers: www.microsoft.com:80,www.yahoo.com:80,www.google.com:80
七、工作原理
执行流程图
对应源码:
AbstractCommand.Class
(Hystrix的统计采用滑动窗口的方式进行统计,将1s拆成10个桶,可以统计滑动窗口内的1s中的数据)
电路开闭发生的具体方式如下:
- 判断固定窗口内的请求数满足某个阈值 (
HystrixCommandProperties.circuitBreakerRequestVolumeThreshold() ). - 并假设错误百分比超过阈值错误百分比
( HystrixCommandProperties.circuitBreakerErrorThresholdPercentage() ) - 然后断路器从 转变 CLOSED 为 OPEN 。
- 当它打开时,它会将针对该断路器的所有请求短路。
- 一段时间后( HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds() ),下一个请
求被允许通过(这是 HALF-OPEN 状态)。如果请求失败,断路器将 OPEN 在睡眠窗口期间返回状
态。如果请求成功,断路器转换到1. CLOSED 中的逻辑再次接管