Hystrix进阶篇:断路器参数配置详解(二)

背景

对于 Hystrix 的配置,我们在平时使用过程经常会出现这样的问题,以为理所应当的配置,其实际上并不会起作用。通过复制粘贴的代码,如果没有经过斟酌,可能永远都没有起到其应该起到的作用。这就是我们为何要将 Hystrix 配置搞清楚的原因。

HystrixCommand 注解属性

@HystrixCommand 注解上的属性。对于 HystrixCommand 注解属性,一般有两种方式,一种是给某个类添加全局配置,一种是针对各个方法添加。

全局配置一般需要借助 @DefaultProperties 注解。除了仅针对方法级别的属性以外(commandKey/observableExecutionMode/fallbackMethod),其他的都可以通过该注解来配置。当一个类中有多个 Hystrix 方法时,这种方式可以减少各个方法上都是相同配置的冗余代码。

局部配置一般是通过在方法上添加 @HystrixCommand 并配置注解参数来实现。这种配置的方式的优先级高于全局配置注解 @DefaultProperties

commandKey

唯一标识一个 Hystrix 方法。默认会取被 @HystrixCommand 注解的方法名。建议要起一个全局独一无二的名称,防止多个方法因为 commandKey 名称重复而相互影响。

groupKey

为 Hystrix 方法的分组标识。默认取当前运行时的方法的类名。

threadPoolKey

用来标识一个线程池的 key。默认取 groupKey 的值。一般来说同一个类中共用同一个线程池。若共用同一个线程池的不同的方法,配置了相同的线程池属性,在第一个方法被执行后线程池的属性就已经被固定,因此会以第一个被执行的方法上的配置为主。

fallbackMethod

方法执行时,出现熔断、超时、异常等错误时会触发该属性配置的回退方法。该方法需要保持与 Hystrix 方法的签名和返回值一致。

ignoreExceptions

指定哪些异常需要被 Hystrix 忽略,忽略的异常会被直接抛到方法上层,不会触发 fallbackMethod 回退方法,也不会参与到失败率的统计中而影响熔断的状态。

observableExecutionMode

TODO 搞清楚Observable调用逻辑。

当 Hystrix 命令被包装成 RxJava 的 Observer 异步执行时,此配置指定了 Observable 被执行的模式,默认是 ObservableExecutionMode.EAGER,Observable 会在被创建后立刻执行,而 ObservableExecutionMode.EAGER模式下,则会产生一个 Observable 被 subscribe 后执行。

@HystrixCommand 注解的方法返回值为 Observable 类型或其子类时生效。默认值为 ObservableExecutionMode.EAGER。我们常见的命令都是同步执行的,此配置项可以不配置。

raiseHystrixExceptions

当配置项中包含 HystrixException.RUNTIME_EXCEPTION 时,所有未被忽略的异常统一包装成 HystrixRuntimeException,方便将异常统一处理。默认值为 {}

defaultFallback

默认回退方法,当配置 fallbackMethod 项时此项不会生效,另外,默认回退方法不能有参数,返回值要与 Hystrix方法的返回值一致。

commandProperties

与 Hystrix 命令属性相关的配置。下文会有详细解释。

threadPoolProperties

与 Hystrix 命令执行线程池相关的属性。下文会有详细解释。

命令属性(commandProperties)

commandProperties 的命令属性是由 @HystrixProperty 数组配置组成。其中 @HystrixProperty 是由 namevalue 两个属性构成,并且这两个属性都是字符串类型。其属性都是以 hystrix.command.* 开头,全局默认属性是 hystrix.command.default.*, 具体方法的属性 hystrix.command.HystrixCommandKey(具体CommandKey名).*。具体的配置如下。

执行属性(Execution)

接下来介绍的属性,是控制 HystrixCommand.run() 方法如何执行的属性。

execution.isolation.strategy

该属性表示指定当前 Hystrix 方法的请求隔离策略。也就是 HystrixCommand.run() 方法执行时采用的隔离策略。有两种隔离方式:线程隔离(THREAD) 和信号量隔离(SEMAPHORE)。默认为线程隔离方式。

线程池隔离 — 表示在隔离的线程中执行,并且并发请求的总数受限于当前线程池中线程的总数。
信号量隔离 — 表示在当前调用的线程中执行,并且并发请求受限于设置的信号量的总数。

HystrixCommand 默认使用线程池隔离(THREAD),HystrixObservableCommand 默认使用信号量隔离(SEMAPHORE)。

通常只有当调用量非常高以至于线程隔离开销太高时,采用信号量隔离的方式。信号量隔离方式通常应用于非网络调用场景。
线程池隔离和信号量隔离

execution.isolation.thread.timeoutInMilliseconds

该属性设置以毫秒为单位的时间,超过该时间后,调用者监听到超时并不再继续执行当前方法命令的执行。默认值为 1000。Hystrix 标记当前 HystrixCommand 为超时状态,并且执行回退逻辑。不过需要注意的是,只有 command.timeout.enabled 开关开启,该配置才会生效。

execution.timeout.enabled

该属性代表着是否开启HystrixCommand.run() 执行时超时。如果开启则到达 execution.isolation.thread.timeoutInMilliseconds 配置的时间后执行超时回退逻辑。默认值为 ture

execution.isolation.thread.interruptOnTimeout

该属性表示 HystrixCommand.run() 方法执行出现超时情况时是否要中断执行。默认值为 true

execution.isolation.thread.interruptOnCancel

该属性表示 HystrixCommand.run() 方法执行被取消时是否要中断执行。默认值为 false

execution.isolation.semaphore.maxConcurrentRequests

该属性是在信号量隔离模式下,允许 HystrixCommand.run() 执行的最大并发数。设置的值只有当 execution.isolation.strategy 设置为 SEMAPHORE 才会生效。超过此并发数的请求会被拒绝。默认值为10

回退属性(Fallback)

回退相关属性是控制 HystrixCommand.getFallback() 方法执行的配置。这些属性会对线程隔离(THREAD)和信号量隔离(SEMAPHORE)都会生效。

fallback.isolation.semaphore.maxConcurrentRequests

回退方法执行的最大并发数。默认值为 10。超过此最大并发数后会抛出 REJECTED_SEMAPHORE_FALLBACK 异常。

fallback.enabled

是否开启方法回退功能。当调用失败或者拒绝发生时是否调用回退方法。默认为 true

断路器属性(CircuitBreaker)

断路器属性是控制 Hystrix 中 HystrixCircuitBreaker 的行为。

circuitBreaker.enabled

断路器开关是否开启。默认值为 true。如果开启,断路器将用于跟踪运行状态,并在断路器跳闸时短路请求。

circuitBreaker.errorThresholdPercentage

该属性设置请求失败错误百分比。到该值后会跳闸电路,将请求断路并执行回退逻辑。默认值 50

在通过滑动窗口获取到当前时间段内 Hystrix 方法执行的失败率后,就需要根据此配置来判断是否要将熔断器打开了。 此配置项默认值是 50,即窗口时间内超过 50% 的请求失败后会打开熔断器将后续请求快速失败。

circuitBreaker.requestVolumeThreshold

在一个滚动窗口中使断路器跳闸的最小请求数。默认值为 20。例如,如果设置值为20,此时如果仅有19个请求进入滚动窗口(假设一个时间窗口为10s),就算是这19个请求全部失败,电路也不会跳闸。

试想如果没有这么一个限制,我们配置了 50% 的请求失败会打开熔断器,窗口时间内只有 3 条请求,恰巧两条都失败了,那么熔断器就被打开了,5s 内的请求都被快速失败。此配置项的值需要根据接口的 QPS 进行计算,值太小会有误打开熔断器的可能,值太大超出了时间窗口内的总请求数,则熔断永远也不会被触发。建议设置为 QPS * 窗口秒数 * 60%
TODO 这里的时间窗口和哪个指标窗口有什么关系?

circuitBreaker.sleepWindowInMilliseconds

断路器跳闸后,所有的请求都会快速失败,但何时服务恢复正常就是下一个要面对的问题。跳闸后,Hystrix 会在经过 circuitBreaker.sleepWindowInMilliseconds 配置的时间后就放行一条请求,如果这条请求执行成功了,说明此时服务很可能已经恢复了正常,那么断路器关闭。如果此请求执行失败,则认为服务依然不可用,断路器继续保持跳闸状态。此配置项指定了断路器打开后经过多长时间允许一次请求尝试执行,默认值是 5000

circuitBreaker.forceOpen

该属性设置为true后,断路器将置于强制开启状态,并且所有的请求将会被拒绝。默认值为 false
该属性优先于下面的 circuitBreaker.forceClosed

circuitBreaker.forceClosed

该属性设置为 true 后,断路器将置于强制关闭状态,并且无论错误请求百分比如何,都允许请求。默认值为 false
circuitBreaker.forceOpen 属性的优先级高于该属性。因此如果设置了 circuitBreaker.forceOpen 属性为 truecircuitBreaker.forceClosed 属性什么都不会做。

指标统计属性(Metrics)

指标属性和 Hystrix 方法执行时捕获的指标相关。

metrics.rollingStats.timeInMilliseconds

设置统计滚动窗口的持续时长,单位为毫秒。这是 Hystrix 保存统计指标供断路器使用和发布的时间。默认值为 10000。即一个滚动窗口默认统计的是 10s 内的请求数据。

滚动窗口被划分为多个桶,并会“滑过”这些桶。例如,假设当前滚动窗口时间为 10s,拆分成 10 个 1秒的桶,下面的图表示的是如何滚动新桶,并关闭旧桶。
滚动窗口工作示例图

metrics.rollingStats.numBuckets

该属性设置滚动统计窗口被划分的桶数。默认值为 10

需要注意的是,metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0 这个表达式必须要为 true,否则会抛异常。也就是说要求这两个指标需要能整除。

metrics.rollingPercentile.enabled

该属性表示方法执行延迟是否应该被跟踪并将其计算为百分位数。如果不计算方法执行延迟,则所有的汇总统计(平均数、百分位数)都返回-1。默认值为 true

metrics.rollingPercentile.timeInMilliseconds

统计方法的执行延迟的滚动窗口的时长。必须要开启 metrics.rollingPercentile.enabled 配置。单位为毫秒。默认值为 60000

metrics.rollingStats.timeInMilliseconds 类似,该滚动窗口也被划分为多个桶,并会“滑过”这些桶。

metrics.rollingPercentile.numBuckets

该属性设置方法执行延迟滚动窗口被划分的桶数。默认值为 6

需要注意的是,metrics.rollingPercentile.timeInMilliseconds % metrics.rollingPercentile.numBuckets == 0 这个表达式必须要为 true,否则会抛异常。也就是说要求这两个指标需要能整除。

metrics.rollingPercentile.bucketSize

该属性设置每个桶被保留的最大执行请求次数。如果在此期间超过了最大执行数后,他们将会从桶的开头进行覆盖。默认值为 100

在默认的情况下,每个桶的时间长度为 10s = 60000ms / 6,但这 10s 内只保留最近的 100 条请求的数据。

如果增加此大小,这也会增加存储值所需的内存量,并增加对列表进行排序以进行百分位计算所需的时间。

metrics.healthSnapshot.intervalInMilliseconds

设置每个拍摄健康快照的间隔时间,允许拍摄健康快照以计算成功和失败百分比以及影响断路器状态。单位为毫秒。默认值为 500

在大容量电路中,连续计算错误百分比可能会占用大量 CPU,因此此属性可用来控制计算频率。

请求上下文属性(Request Context)

请求上下文属性涉及在 HystrixCommand 中使用的 HystrixRequestContext 的功能。

requestCache.enabled

是否开启请求结果缓存功能。但它并不意味着我们的每个请求都会被缓存。缓存请求结果和从缓存中获取结果都需要我们配置 @CacheKey,并且在方法上使用 @CacheResult 注解声明一个缓存上下文。默认值为 true

requestLog.enabled

是否开启请求日志。默认值为 true

线程池属性(threadPoolProperties)

线程池属性是控制 Hystrix 请求执行的线程池的行为。注意这些名称和 JAVA 中的线程池是匹配的。

线程池属性都是以 hystrix.threadpool.* 开头,全局默认属性是 hystrix.threadpool.default.*, 具体方法的属性 hystrix.threadpool.HystrixCommandKey(具体CommandKey名).*

绝大多数情况默认值 10 个线程就可以了,一般可以设置得更小。如果要调整线程池的大小,有个基本的计算公式:
健康时每秒峰值请求数 * 第99个百分位延迟秒数 + 喘息空间线程数 (TODO)

下面用一个示例说明公式的使用。

基本的原则是尽可能让线程池保持最小,因为这是减轻负载和阻止资源在发生延迟时而被阻塞的主要方法。

Netflix API has 30+ of its threadpools set at 10, two at 20, and one at 25.

断路器线程池示意图
上图显示了一个示例配置,其中依赖项没有理由达到第 99.5 个百分位,因此它在网络超时层将其缩短并立即重试,并期望它在大多数时间获得中等延迟,并且将能够在 300 毫秒线程超时内完成这一切。

如果依赖项有正当理由有时会达到第 99.5 个百分位数(例如延迟生成的缓存未命中),则网络超时将需要设置得比它高,例如 325 毫秒,重试 0 或 1 次,线程超时设置得更高(350ms+ )

线程池大小为 10 以处理突发的 99% 请求,但当一切正常时,此线程池通常在任何给定时间只有 1 或 2 个线程处于活动状态,以服务大多数 40 毫秒的中值调用。

当你正确配置它时,HystrixCommand 层的超时应该会很少见,但保护是存在的,以防网络延迟以外的其他因素影响时间,或者在最坏的情况下连接+读取+重试+连接+读取的组合仍然存在超过配置的整体超时时间。

当性能特征发生变化或发现问题时,您可以根据需要实时更改配置,在出现问题或配置错误时,所有这些都没有关闭整个应用程序的风险。
TODO 这里需要读源码确认

coreSize

核心线程池大小。默认值为 10。TODO,计算公式有点疑问。

maximumSize

最大线程池大小。默认值为 10。该属性只有设置了 allowMaximumSizeToDivergeFromCoreSize 属性后才会生效。

maxQueueSize

线程池最大线程排队数。默认值为 -1。如果值为 -1,会使用 SynchronousQueue,此时队列大小为 0,Hystrix 不会向队列内排队线程;如果值为正数则会使用 LinkedBlockingQueue,这时候 size 是一个固定大小的队列,当核心线程忙碌时,会将作业暂时存放在此队列。

queueSizeRejectionThreshold

该属性设置队列大小拒绝阈值。人为控制最大队列大小,即使未达到 maxQueueSize 也会发生拒绝。存在此属性是因为 BlockingQueuemaxQueueSize 无法动态更改。默认值为5。所以有时候只设置了 maxQueueSize 也不会起作用。注意,当 maxQueueSize==-1 时,该属性不生效。

keepAliveTimeMinutes

如果 coreSize < maximumSize,则此属性控制线程在被释放之前将闲置多长时间,单位分钟。默认值为 1

allowMaximumSizeToDivergeFromCoreSize

只有配置了该属性, maximumSize 才会生效。值的设置可以比 coreSize 相等或者大。类似于 JAVA 的线程池,只有在队列满后才会启用最大线程数,之后线程不活动后会定时回收线程(根据 keepAliveTimeInMinutes 配置的时间)。默认值为 false

metrics.rollingStats.timeInMilliseconds

设置统计滚动窗口的持续时长,单位为毫秒。默认值为 10000。这是线程池的统计指标会被保留的时间。和上面的 command 配置类似,该窗口也会被拆分成多个桶。

metrics.rollingStats.numBuckets

该属性设置滚动统计窗口被划分的桶数。默认值为 10

需要注意的是,metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0 这个表达式必须要为 true,否则会抛异常。也就是说要求这两个指标需要能整除。

合并请求属性(CollapserProperties)

合并请求属性是用来控制 HystrixCollapser 的行为的。其属性都是以 hystrix.collapser.* 开头,全局默认属性是 hystrix.collapser.default.*, 具体方法的属性 hystrix.collapser.HystrixCommandKey(具体CommandKey名).*。具体的配置如下。

maxRequestsInBatch

该属性设置允许在触发批处理执行前批处理中允许的最大请求数。默认值为 Integer.MAX_VALUE

timerDelayInMilliseconds

此属性设置创建批处理后触发其执行的毫秒数。默认值为 10

requestCache.enabled

在合并请求中决定 HystrixCollapser.execute() HystrixCollapser.queue() 方法调用中请求缓存是否生效。

疑问

  • 验证是否方法的返回值类型,决定当前的方法是属于异步、同步、观察者模式。
  • 确定根据方法返回类型判断当前是否是同步、异步、观察者模式。
  • Future 请求的原理。
  • 多个事情同时开始,最终需要等待所有事情完成后在做多个相互依赖的事情。比如炒菜。
  • 理清楚 commandKey、groupKey、threadPoolKey之间的关系。
  • groupKey 会作为线程池key名称的备选。也就是当不配置threadPoolKey名称的时候,会赋值 groupKey 给线程名称。
  • commandKey 只会使用方法名或者指定名称,一次要保证唯一性,否则会有问题。
  • RxJava编码风格,要求可以看懂 Hystrix 代码逻辑。
  • 断路器的滚动窗口在什么地方统计,其时间计算方式是什么?
  • 两个滚动窗口,这些作用是什么?分别是用来做什么的?
  • 方法执行延迟的滚动窗口?
  • 线程池的滚动窗口又是具体干啥的?

缓存使用场景

合并请求使用场景

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值