在我们的日常开发中,有些时候需要和第三方系统进行对接操作,或者调用其他系统的 api 接口,但是我们不能保证这些第三方系统的接口一定是稳定的,当系统中产生大量的流量来访问这些第三方接口,这些第三方系统的接口响应慢时,如何保证我们自己的系统不被这些第三方系统的接口耗费完系统的资源,导致我们自己的系统崩溃。为了避免这种情况的发生,我们应该将这些第三方系统的接口进行隔离访问,将他们分配到一个单独的线程池中进行处理,或给他们固定的资源,并且当出现失败时,做好相应的回退处理。 此时我们就可以借助 hystrix 来完成这个需求,当然 hystrix 能完成的功能远不止如此。 hystrix的Wiki地址
此文简单记录一下 hystrix 的各种配置,并在文末给出一个可以运行的 例子
一、hystrix的各种配置
public class SelectProductCommand extends HystrixCommand<Product> {
private ProductService productService;
private String productId;
public SelectProductCommand(ProductService productService, String productId) {
super(
Setter
// 配置全局唯一标识服务分组的名称 当我们进行监控时,相同分组的服务会聚合在一起,必填项
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("group-product"))
// command key ,如果不进行配置,默认为类的 SimpleName,最好唯一
.andCommandKey(HystrixCommandKey.Factory.asKey("command-selectOne(String)"))
// 配置命令的一些参数
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
// 是否启动超时处理 默认为 true
.withExecutionTimeoutEnabled(true)
// 超时时间为 10s
.withExecutionTimeoutInMilliseconds(10000)
// 使用线程池进行隔离
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
// 隔离测试为 THREAD 时,执行线程执行超时时,是否进行中断处理 默认为 true
.withExecutionIsolationThreadInterruptOnTimeout(true)
// 当 Future#cancel(true) 是否进行中断处理 默认为 false
.withExecutionIsolationThreadInterruptOnFutureCancel(true)
// 打开短路器
.withCircuitBreakerEnabled(true)
// 如果在一个采样时间窗口内,失败率超过该配置,则自动打开熔断开关实现降级处理,即快速失败。默认配置下采样周期为10s,失败率为50%
.withCircuitBreakerErrorThresholdPercentage(50)
// 在短路器闭合情况下,在进行失败率判断之前,一个采样周期内必须进行至少N个请求才能进行采样统计,目的是有足够的采样使得失败率计算正确,默认为20
.withCircuitBreakerRequestVolumeThreshold(60)
// 短路器闭合的重试时间窗口,且在该时间窗口内只允许一次重试。即在熔断开关打开后,在该时间窗口允许有一次重试,如果重试成功,则将重置Health采样统计并闭合断路器开关实现快速恢复,否则断路器开关还是打开状态,执行快速失败。
.withCircuitBreakerSleepWindowInMilliseconds(10000)
// 是否强制关闭断路器,如果强制关闭则请求不会进行 fallback 处理
.withCircuitBreakerForceClosed(false)
// 是否强制打开短路器,如果打开了那么将会直接进行降级处理
.withCircuitBreakerForceOpen(false)
// 是否启用降级处理 默认是 true
.withFallbackEnabled(true)
// fallback方法的信号量配置,配置getFallback方法并发请求的信号量,如果请求超过了并发信号量限制,则不再尝试调用getFallback方法,而是快速失败,默认信号量为10
.withFallbackIsolationSemaphoreMaxConcurrentRequests(100)
)
// 配置全局唯一标识线程池的名称 相同名称的线程池是同一个 如果不进行配置, 默认是 分组的名称
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("select-product-thread-pool"))
// 线程池属性配置
.andThreadPoolPropertiesDefaults(
HystrixThreadPoolProperties.Setter()
// 配置核心线程数的大小
.withCoreSize(5)
// 配置最大的线程数大小
.withMaximumSize(10)
// 允许最大线程数超过核心线程数,默认是 false ,如果这个值不为 true ,则上方配置的 withMaximumSize(10) 不会生效
.withAllowMaximumSizeToDivergeFromCoreSize(true)
// 线程池中空闲线程的生存时间
.withKeepAliveTimeMinutes(5)
// 配置线程池队列的最大大小
.withMaxQueueSize(500)
// 限制当前队列的大小,通过改变这个参数,可以实现动态改变队列的大小 当队列的大小超过 这个值 时会fallback
.withQueueSizeRejectionThreshold(490)
)
);
this.productService = productService;
this.productId = productId;
}
@Override
protected Product run() throws Exception {
log.info("----> Run Thread Name:{}", Thread.currentThread().getName());
return productService.selectOne(productId);
}
/**
* 1、最大并发数受 FallbackIsolationSemaphoreMaxConcurrentRequests 限制,当超过这个值时,不会进行 fallback 处理,直接失败
* 2、该方法最好不要进行网络访问,应该返回降级数据
* 3、如果必须要走网络请求,那么最好调用另外一个 Command 命令
*
* @return
*/
@Override
protected Product getFallback() {
log.info("----> FallBack Thread Name:{}", Thread.currentThread().getName());
Optional.ofNullable(getFailedExecutionException()).ifPresent(exception -> log.error(exception.getMessage(), exception));
return Product.builder().productName("服务降级").build();
}
}
hystrix使用线程池隔离时,线程池大小线程的配置:
每秒的访问峰值 * 99%的响应时间(s) + 预留线程数
二、使用 @HystrixCommand 的方式和注意事项
@HystrixCommand配置参考代码:https://gitee.com/huan1993/hystrix/tree/master/order-consumer-annotation-hystrix
三、进入 fallback 的时机
1、执行方法抛出了异常,非 HystrixBadRequestException 异常
2、方法执行超时了
3、断路器打开了
4、hystrix线程池拒绝了
四、隔离机制
hystrix中存在2中隔离机制,THREAD 隔离和 SEMAPHORE(信号量) 隔离。
1、THREAD 隔离,它在一个单独的线程上执行,和调用线程不是在同一个线程上
2、SEMAPHORE 隔离,它和调用线程在同一个线程上
3、当我们继承 HystrixCommand 类时,建议使用 THREAD 隔离
4、当我们继承 HystrixObservableCommand 类时,建议使用 SEMAPHORE 隔离
5、一般情况下,我们使用线程隔离即可,SEMAPHORE隔离一般只适用非网络调用。