如果某程序或class要使用Hystrix,只需简单继承HystrixCommand/HystrixObservableCommand并重写run()/construct(),然后调用程序实例化此class并执execute()/queue()/observe()/toObservable()。
一.添加依赖
com.netflix.hystrix
hystrix-core
1.5.18
二.开启熔断
启动类加上注解,开启熔断
@EnableHystrix
三.新建command类,继承:HystrixCommand
// HelloWorldHystrixCommand要使用Hystrix功能
public class HelloWorldHystrixCommand extends HystrixCommand {
private final String name;
public HelloWorldHystrixCommand(String name) {
super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”));
this.name = name;
}
// 如果继承的是HystrixObservableCommand,要重写Observable construct()
@Override
protected String run() {
return “Hello " + name;
}
}
/* 调用程序对HelloWorldHystrixCommand实例化,执行execute()即触发HelloWorldHystrixCommand.run()的执行 /
String result = new HelloWorldHystrixCommand(“HLX”).execute();
System.out.println(result); // 打印出Hello HLX
四.fallback(降级)
使用fallback机制很简单,继承HystrixCommand只需重写getFallback(),继承HystrixObservableCommand只需重写resumeWithFallback(),比如HelloWorldHystrixCommand加上下面代码片段:
@Override
protected String getFallback() {
return “fallback: " + name;
}
fallback实际流程是当run()/construct()被触发执行时或执行中发生错误时,将转向执行getFallback()/resumeWithFallback()。4种错误情况将触发fallback:
1.非HystrixBadRequestException异常:当抛出HystrixBadRequestException时,调用程序可以捕获异常,没有触发getFallback(),而其他异常则会触发getFallback(),调用程序将获得getFallback()的返回
2.run()/construct()运行超时:使用无限while循环或sleep模拟超时,触发了getFallback()
3.熔断器启动:配置10s内请求数大于3个时就启动熔断器,请求错误率大于80%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()。
4.线程池/信号量已满:配置线程池数目为3,然后先用一个for循环执行queue(),触发的run()sleep 2s,然后再用第2个for循环执行execute(),发现所有execute()都触发了fallback,这是因为第1个for的线程还在sleep,占用着线程池所有线程,导致第2个for的所有命令都无法获取到线程
五.隔离策略
hystrix提供了两种隔离策略:线程池隔离和信号量隔离。hystrix默认采用线程池隔离。
1.线程池隔离:不同服务通过使用不同线程池,彼此间将不受影响,达到隔离效果。
2.信号量隔离:线程隔离会带来线程开销,有些场景(比如无网络请求场景)可能会因为用开销换隔离得不偿失,为此hystrix提供了信号量隔离,当服务的并发数大于信号量阈值时将进入fallback。
六.熔断机制
熔断机制相当于电路的跳闸功能,举个栗子,我们可以配置熔断策略为当请求错误比例在10s内>50%时,该服务将进入熔断状态,后续请求都会进入fallback。
比如通过withCircuitBreakerRequestVolumeThreshold配置10s内请求数超过3个时熔断器开始生效,通过withCircuitBreakerErrorThresholdPercentage配置错误比例>80%时开始熔断,然后for循环执行execute()触发run(),在run()里,如果name是小于10的偶数则正常返回,否则超时,通过多次循环后,超时请求占所有请求的比例将大于80%,就会看到后续请求都不进入run()而是进入getFallback(),因为不再打印"running run():” + name了。
七. 参数配置
1.基于setter方法的配置(相关参数均配置在阿波罗中):
private static Setter setter() {
return Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“GroupBuyMainSearchHomeServiceGroup”))
.andCommandKey(HystrixCommandKey.Factory.asKey(“GroupBuyMainSearchHomeServiceCommond”))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“GroupBuyMainSearchHomeServiceCommondThreadPool”))
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
//设置coreSize小于maximumSize的线程池能够支持maximumSize的并发数,
.withAllowMaximumSizeToDivergeFromCoreSize(
ConfigService.getAppConfig().getBooleanProperty(“mainSearch.hystrix.allowMaximumSizeToDivergeFromCoreSize”,true))
//设置线程池核心线程数
.withCoreSize(Runtime.getRuntime().availableProcessors() * 2)
/设置线程池非核心线程活跃时间/ .withKeepAliveTimeMinutes(ConfigService.getAppConfig().getIntProperty(“mainSearch.threadPool.keepAliveTimeMinutes”,5))
/设置线程池最大线程数/ .withMaximumSize(ConfigService.getAppConfig().getIntProperty(“mainSearch.threadPool.maximumSize”,16))
/设置线程池任务队列大小/ .withMaxQueueSize(ConfigService.getAppConfig().getIntProperty(“mainSearch.threadPool.maxQueueSize”,200))
/设置线程池拒绝阈值/ .withQueueSizeRejectionThreshold(ConfigService.getAppConfig().getIntProperty(“mainSearch.threadPool.queueSizeRejectionThreshold”,200))
/
* metrics.rollingStats.timeInMilliseconds
* 设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短。
* 默认值:10000(毫秒)
*/
.withMetricsRollingStatisticalWindowInMilliseconds(
ConfigService.getAppConfig().getIntProperty(“mainSearch.hystrix.metricsRollingStatisticalWindow”, 10000)))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
//设置断路器是否生效
.withCircuitBreakerEnabled(
ConfigService.getAppConfig().getBooleanProperty(“mainSearch.hystrix.breakerEnabled”,true))
//设置异常比例达到多少,才触发断路,默认50%
.withCircuitBreakerErrorThresholdPercentage(
ConfigService.getAppConfig().getIntProperty(“mainSearch.hystrix.thresholdPercentage”, 50))
//设置断路后多少时间内直接reject请求,之后进入half-open状态,默认5000ms
.withCircuitBreakerSleepWindowInMilliseconds(
ConfigService.getAppConfig().getIntProperty(” mainSearch.hystrix.resume.mills", 5000))
//设置滑动窗口中,最少有多少个请求,才可能触发断路
.withCircuitBreakerRequestVolumeThreshold(
ConfigService.getAppConfig().getIntProperty(“mainSearch.hystrix.breakerRequestNomber”,10))
//设置是否开启超时熔断
.withExecutionTimeoutEnabled(
ConfigService.getAppConfig().getBooleanProperty(“mainSearch.hystrix.breakerTimeoutEnabled”,true))
//设置超时时间,默认1000毫秒(1秒)
.withExecutionTimeoutInMilliseconds(
ConfigService.getAppConfig().getIntProperty(" mainSearch.hystrix.timeoutInMilliseconds", 1000))
//设置隔离策略:线程池/信号量
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD));
}
2.基于注解的配置:
@HystrixCommand(fallbackMethod = “fallbackMethod”,
groupKey = “strGroupCommand”,
commandKey = “strCommand”,
threadPoolKey = “strThreadPool”,
commandProperties = {
// 设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离
@HystrixProperty(name = “execution.isolation.strategy”, value = “THREAD”),
// 当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
@HystrixProperty(name = “execution.isolation.semaphore.maxConcurrentRequests”, value = “10”),
// 配置命令执行的超时时间
@HystrixProperty(name = “execution.isolation.thread.timeoutinMilliseconds”, value = “10”),
// 是否启用超时时间
@HystrixProperty(name = “execution.timeout.enabled”, value = “true”),
// 执行超时的时候是否中断
@HystrixProperty(name = “execution.isolation.thread.interruptOnTimeout”, value = “true”),
// 执行被取消的时候是否中断
@HystrixProperty(name = “execution.isolation.thread.interruptOnCancel”, value = “true”),
// 允许回调方法执行的最大并发数
@HystrixProperty(name = “fallback.isolation.semaphore.maxConcurrentRequests”, value = “10”),
// 服务降级是否启用,是否执行回调函数
@HystrixProperty(name = “fallback.enabled”, value = “true”),
// 是否启用断路器
@HystrixProperty(name = “circuitBreaker.enabled”, value = “true”),
// 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候,如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
@HystrixProperty(name = “circuitBreaker.requestVolumeThreshold”, value = “20”),
// 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过 circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50, 就把断路器设置为 “打开” 状态,否则就设置为 “关闭” 状态。
@HystrixProperty(name = “circuitBreaker.errorThresholdPercentage”, value = “50”),
// 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后,会将断路器置为 “半开” 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 “打开” 状态,如果成功就设置为 “关闭” 状态。
@HystrixProperty(name = “circuitBreaker.sleepWindowinMilliseconds”, value = “5000”),
// 断路器强制打开
@HystrixProperty(name = “circuitBreaker.forceOpen”, value = “false”),
// 断路器强制关闭
@HystrixProperty(name = “circuitBreaker.forceClosed”, value = “false”),
// 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
@HystrixProperty(name = “metrics.rollingStats.timeinMilliseconds”, value = “10000”),
// 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据设置的时间窗长度拆分成多个 “桶” 来累计各度量值,每个"桶"记录了一段时间内的采集指标。
// 比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
@HystrixProperty(name = “metrics.rollingStats.numBuckets”, value = “10”),
// 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
@HystrixProperty(name = “metrics.rollingPercentile.enabled”, value = “false”),
// 该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
@HystrixProperty(name = “metrics.rollingPercentile.timeInMilliseconds”, value = “60000”),
// 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
@HystrixProperty(name = “metrics.rollingPercentile.numBuckets”, value = “60000”),
// 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数,
// 就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行,
// 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
@HystrixProperty(name = “metrics.rollingPercentile.bucketSize”, value = “100”),
// 该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。
@HystrixProperty(name = “metrics.healthSnapshot.intervalinMilliseconds”, value = “500”),
// 是否开启请求缓存
@HystrixProperty(name = “requestCache.enabled”, value = “true”),
// HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
@HystrixProperty(name = “requestLog.enabled”, value = “true”),
},
threadPoolProperties = {
// 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
@HystrixProperty(name = “coreSize”, value = “10”),
// 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,否则将使用 LinkedBlockingQueue 实现的队列。
@HystrixProperty(name = “maxQueueSize”, value = “-1”),
// 该参数用来为队列设置拒绝阈值。 通过该参数, 即使队列没有达到最大值也能拒绝请求。
// 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
@HystrixProperty(name = “queueSizeRejectionThreshold”, value = “5”),
})
八.降级测试:
1.run()方法发生异常降级:
/**
* 异常降级
* **非HystrixBadRequestException异常:当抛出HystrixBadRequestException时,
* **调用程序可以捕获异常,此时不会触发fallback,而其他异常则会触发fallback,
* **调用程序将获得fallback逻辑的返回结果。
*/
@Slf4j
public class HystrixTest extends HystrixCommand<String> {
private final int n;
protected HystrixTest(int n) {
super(HystrixTest.setter());
this.n = n;
}
@Override
protected String run() throws Exception {
log.info("n = "+ n + "-run()方法开始执行 --> " + Thread.currentThread().getName());
// 制造异常:执行run()方法发生异常时,会转向执行getFallback方法
int i = 1 / n;
// 如果此处异常被捕获,将不会进入getFallback()
// try {
// int i = 1 / n;
// } catch (Exception e) {
// log.error("异常:" + e.getMessage());
// }
log.info("n = "+ n + "-run方法执行结束()--> " + Thread.currentThread().getName());
return "n = "+ n + "执行成功";
}
@Override
protected String getFallback() {
log.error("n = "+ n + "-异常降级! C--> " + Thread.currentThread().getName());
return "n = "+ n + "执行失败";
}
private static Setter setter() {
return Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupBuyMainSearchHomeServiceGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommond"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommondThreadPool"))
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withAllowMaximumSizeToDivergeFromCoreSize(true)
.withCoreSize(Runtime.getRuntime().availableProcessors() * 2)
.withKeepAliveTimeMinutes(5)
.withMaximumSize(16)
.withMaxQueueSize(200)
.withQueueSizeRejectionThreshold(200)
/*
* metrics.rollingStats.timeInMilliseconds
* 设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短。
* 默认值:10000(毫秒)
*/
.withMetricsRollingStatisticalWindowInMilliseconds(10000))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
/*
* execution.isolation.thread.interruptOnTimeout
* 设置HystrixCommand的执行是否在超时发生时被中断。
* 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作。
* 默认值:true
*/
.withExecutionIsolationThreadInterruptOnTimeout(false)
// 异常比例达到多少,才触发断路,默认50%
.withCircuitBreakerErrorThresholdPercentage(50)
// 断路后多少时间内直接reject请求,之后进入half-open状态,默认5000ms
.withCircuitBreakerSleepWindowInMilliseconds(5000)
// 滑动窗口中,最少有多少个请求,才可能触发断路
.withCircuitBreakerRequestVolumeThreshold(20)
/*
* execution.isolation.thread.timeoutInMilliseconds
* 设置调用者等待命令执行的超时限制,超过此时间,HystrixCommand被标记为TIMEOUT,
* 并执行回退逻辑。
* 默认值:1000(毫秒)
*/
.withExecutionTimeoutInMilliseconds(500)
//开启超时熔断
.withExecutionTimeoutEnabled(true)
//设置隔离策略
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD));
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
if (i == 3) {
i = 0;
}
HystrixTest command = new HystrixTest(i);
log.info(command.execute());
Thread.sleep(1000);
}
}
}
2.超时降级
@Slf4j
public class HystrixTest2 extends HystrixCommand {
private final static Logger logger = LoggerFactory.getLogger(HystrixTest2.class);
private final int n;
protected HystrixTest2(int n) {
super(HystrixTest2.setter());
this.n = n;
}
@Override
protected String run() throws Exception {
logger.info("n = "+ n + "-run()方法开始执行 --> " + Thread.currentThread().getName());
if (n == 0) {
// 设置超时
Thread.sleep(1000);
}
// 设置withExecutionIsolationThreadInterruptOnTimeout(false)后面代码将会继续执行
logger.info("n = "+ n + "-run()方法执行结束 --> " + Thread.currentThread().getName());
return "n = "+ n + "执行成功";
}
@Override
protected String getFallback() {
logger.error("n = "+ "超时降级! --> " + Thread.currentThread().getName());
return "n = "+ n + "执行失败";
}
private static Setter setter() {
return Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupBuyMainSearchHomeServiceGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommond"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommondThreadPool"))
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withAllowMaximumSizeToDivergeFromCoreSize(true)
.withCoreSize(Runtime.getRuntime().availableProcessors() * 2)
.withKeepAliveTimeMinutes(5)
.withMaximumSize(16)
.withMaxQueueSize(200)
.withQueueSizeRejectionThreshold(200)
/*
* metrics.rollingStats.timeInMilliseconds
* 设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短。
* 默认值:10000(毫秒)
*/
.withMetricsRollingStatisticalWindowInMilliseconds(10000))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
/*
* execution.isolation.thread.interruptOnTimeout
* 设置HystrixCommand的执行是否在超时发生时被中断。
* 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作。
* 默认值:true
*/
.withExecutionIsolationThreadInterruptOnTimeout(false)
// 异常比例达到多少,才触发断路,默认50%
.withCircuitBreakerErrorThresholdPercentage(50)
// 断路后多少时间内直接reject请求,之后进入half-open状态,默认5000ms
.withCircuitBreakerSleepWindowInMilliseconds(5000)
// 滑动窗口中,最少有多少个请求,才可能触发断路
.withCircuitBreakerRequestVolumeThreshold(20)
/*
* execution.isolation.thread.timeoutInMilliseconds
* 设置调用者等待命令执行的超时限制,超过此时间,HystrixCommand被标记为TIMEOUT,
* 并执行回退逻辑。
* 默认值:1000(毫秒)
*/
.withExecutionTimeoutInMilliseconds(500)
//开启超时熔断
.withExecutionTimeoutEnabled(true)
//设置隔离策略
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD));
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
if (i == 5) {
i = 0;
}
HystrixTest2 command = new HystrixTest2(i);
// 超时执行getFallback
logger.info(command.execute());
Thread.sleep(1000);
}
}
}
3.断路器打开降级
public class HystrixTest3 extends HystrixCommand {
private final static Logger logger = LoggerFactory.getLogger(HystrixTest3.class);
private final int n;
protected HystrixTest3(int n) {
super(HystrixTest3.setter());
this.n = n;
}
@Override
protected String run() throws Exception {
logger.info("n = "+n + " 断路器未打开-run方法开始执行 --> " + Thread.currentThread().getName());
/*
n等于0时成功执行完成,n等于1--4的时候会执行run方法超时,然后执行getfallback,此时请求数少于5,断路器不会打开
n等于5的时候,因为此时已经达到了5个请求,并且4个超时失败了,失败率为80%,大于配置的50%,因此断路器会打开,
n等于5--9的时候由于断路器打开,则不会执行run方法,而是直接执行getfallback方法。
n等于10时,由于配置的断路器恢复half-open状态时间为5秒,而每个请求休眠时间为1.2秒,此时已经达到恢复时间,
因此n等于10 时可以成功执行玩run方法
后面依次类推。。。。。。
*/
if (n %10 != 0) {
// 设置超时
Thread.sleep(1200);
}
// 设置withExecutionIsolationThreadInterruptOnTimeout(true)后面代码将中断执行
logger.info("n = "+n + " 断路器未打开-run方法执行结束 --> " + Thread.currentThread().getName());
return "n = "+n + "执行成功";
}
@Override
protected String getFallback() {
logger.error("熔断降级! --> " + Thread.currentThread().getName());
return n + "执行失败";
}
private static Setter setter() {
return Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupBuyMainSearchHomeServiceGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommond"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommondThreadPool"))
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withAllowMaximumSizeToDivergeFromCoreSize(true)
.withCoreSize(Runtime.getRuntime().availableProcessors() * 2)
.withKeepAliveTimeMinutes(20)
.withMaximumSize(20)
.withMaxQueueSize(200)
.withQueueSizeRejectionThreshold(200)
/*
* metrics.rollingStats.timeInMilliseconds
* 设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短。
* 默认值:10000(毫秒)
*/
.withMetricsRollingStatisticalWindowInMilliseconds(10000))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
// ** 断路器(Circuit Breaker)属性配置 **
/*
* circuitBreaker.enabled
* 设置断路器是否生效
* 默认值:true
*/
.withCircuitBreakerEnabled(true)
/*
* execution.isolation.thread.interruptOnTimeout
* 设置HystrixCommand的执行是否在超时发生时被中断。
* 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作。
* 默认值:true
*/
.withExecutionIsolationThreadInterruptOnTimeout(true)
// 异常比例达到多少,才触发断路,默认50%
.withCircuitBreakerErrorThresholdPercentage(50)
// 断路后多少时间内直接reject请求,之后进入half-open状态,默认5000ms
.withCircuitBreakerSleepWindowInMilliseconds(5000)
// 滑动窗口中,最少有多少个请求,才可能触发断路
.withCircuitBreakerRequestVolumeThreshold(5)
/*
* execution.isolation.thread.timeoutInMilliseconds
* 设置调用者等待命令执行的超时限制,超过此时间,HystrixCommand被标记为TIMEOUT,
* 并执行回退逻辑。
* 默认值:1000(毫秒)
*/
.withExecutionTimeoutInMilliseconds(1000)
//开启超时熔断
.withExecutionTimeoutEnabled(true)
//设置隔离策略
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD));
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
try {
Thread.sleep(1000);
logger.info(new HystrixTest3(i).execute());
} catch (Exception e) {
logger.error("异常:" + e.getMessage(), e);
}
}
}
}
4.线程池耗尽拒绝服务降级
public class HystrixTest4 extends HystrixCommand {
private final static Logger logger = LoggerFactory.getLogger(HystrixTest4.class);
private final int n;
protected HystrixTest4(int n) {
super(HystrixTest4.setter());
this.n = n;
}
@Override
protected String run() throws Exception {
/*
线程池配置核心线程为:处理器*2=16,最大线程为20,线程活跃时间为1分钟(20-16=4【非核心线程】),任务队列为-1,不存储任务
run()方法执行休眠30秒,main方法每一秒产生一个新线程调用执行run()方法,即前20(0-19)个线程可以执行run()放,然后进入30秒的休眠
第20个线程开始,被线程池拒绝执行,然后执行getFallback()方法,
第30个线程开始,陆续每秒会有一个线程结束休眠,然后后面的线程可以开始执行run()方法。
*/
logger.info("n = "+n + "-run方法执行 --> " + Thread.currentThread().getName());
Thread.sleep(30000);
logger.info("n = "+n + "-run方法结束 --> " + Thread.currentThread().getName());
return "n = "+n + "执行完毕";
}
@Override
protected String getFallback() {
logger.error("n = "+n + "线程池/信号量已满 FALLBACK --> !");
return "n = "+n + "FALLBACK";
}
public static class ThreadRunner extends Thread {
private int i;
public ThreadRunner(int i) {
this.i = i;
}
@Override
public void run() {
logger.info(new HystrixTest4(i).execute());
}
}
public static void main(String[] args) throws Exception {
// 关闭HystrixCommand执行的超时限制,设置withExecutionTimeoutEnabled(false)
// withCoreSize(10)
for (int i = 0; i < Integer.MAX_VALUE; i++) {
Thread thread = new ThreadRunner(i);
thread.start();
Thread.sleep(1000);
}
}
private static Setter setter() {
return Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupBuyMainSearchHomeServiceGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommond"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GroupBuyMainSearchHomeServiceCommondThreadPool"))
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withAllowMaximumSizeToDivergeFromCoreSize(true)
.withCoreSize(Runtime.getRuntime().availableProcessors() * 2)
.withKeepAliveTimeMinutes(1)
.withMaximumSize(20)
.withMaxQueueSize(-1)
.withQueueSizeRejectionThreshold(5)
/*
* metrics.rollingStats.timeInMilliseconds
* 设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短。
* 默认值:10000(毫秒)
*/
.withMetricsRollingStatisticalWindowInMilliseconds(10000)
/*
* metrics.rollingStats.numBuckets
* 设置滚动的统计窗口被分成的桶(bucket)的数目。
* 注意:”metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0”必须为true,否则会抛出异常。
* 默认值:10
*/
.withMetricsRollingStatisticalWindowBuckets(10))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
// ** 断路器(Circuit Breaker)属性配置 **
/*
* circuitBreaker.enabled
* 设置断路器是否生效
* 默认值:true
*/
.withCircuitBreakerEnabled(true)
/*
* execution.isolation.thread.interruptOnTimeout
* 设置HystrixCommand的执行是否在超时发生时被中断。
* 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作。
* 默认值:true
*/
.withExecutionIsolationThreadInterruptOnTimeout(true)
// 异常比例达到多少,才触发断路,默认50%
.withCircuitBreakerErrorThresholdPercentage(50)
// 断路后多少时间内直接reject请求,之后进入half-open状态,默认5000ms
.withCircuitBreakerSleepWindowInMilliseconds(5000)
// 滑动窗口中,最少有多少个请求,才可能触发断路
.withCircuitBreakerRequestVolumeThreshold(5)
/*
* execution.isolation.thread.timeoutInMilliseconds
* 设置调用者等待命令执行的超时限制,超过此时间,HystrixCommand被标记为TIMEOUT,
* 并执行回退逻辑。
* 默认值:1000(毫秒)
*/
.withExecutionTimeoutInMilliseconds(50000)
//开启超时熔断
.withExecutionTimeoutEnabled(true)
// ** 回退属性 **
// 下面的属性控制HystrixCommand.getFallback()执行。
// 这些属性对ExecutionIsolationStrategy.THREAD和ExecutionIsolationStrategy.SEMAPHORE都有效。
/*
* fallback.isolation.semaphore.maxConcurrentRequests
* 设置调用线程产生的HystrixCommand.getFallback()方法的允许最大请求数目。
* 如果达到最大并发数目,后续请求将会被拒绝,如果没有实现回退,则抛出异常。
* (这里需要注意一点,这个变量的命名不是很规范,它实际上对THREAD和SEMAPHORE两种隔离策略都生效)
* 默认值:10
*/
// .withFallbackIsolationSemaphoreMaxConcurrentRequests(10)
/*
* fallback.enabled
* 该属性决定当前的调用故障或者拒绝发生时,是否调用HystrixCommand.getFallback()。
* 默认值:true
*/
// .withFallbackEnabled(true)
//设置隔离策略
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
// ** 请求上下文属性配置 **
/*
* requestCache.enabled
* 设置HystrixCommand.getCacheKey()是否启用,由HystrixRequestCache通过请求缓存提供去重复数据功能。
* (请求结果缓存需要配合HystrixRequestContext使用,具体应用可以自行查阅)
* 默认值:true
*/
.withRequestCacheEnabled(true)
/*
* requestLog.enabled
* 设置HystrixCommand执行和事件是否要记录日志到HystrixRequestLog。
* 默认值:true
*/
.withRequestLogEnabled(true));
}
}
九.主搜调用量统计及熔断参数设置
调用统计:
序号 时间段 时间段内调用量范围 时间段内最大异常 最大异常时调用量 最大异常时平均每秒调用量 最大异常时平均每秒异常数
1 4月28号 12:40-12:55 600-1000 12:46共18个 936次 15.6次 0.3
2 4月28号 14:50-15:05 1000-1500 14:56共26个 1103次 18.3次 0.43
3 4月29号 13:35-13:50 700-1200 13:41共23个 1030次 17.1次 0.38
4 4月29号 13:49-14:04 900-1200 14:00共35个 1239次 20.6次 0.58
5 4月29号 14:04-14:19 300-1400 14:14共7个 1346次 22.4次 0.11
6 4月29号 14:19-14:34 600-1500 14:30共30个 1305次 21.7次 0.5
7 5月2号 22:38-22:53 50-350 22:50共3个 306次 5.1次 0.05
8 5月3号 13:14-13:29 400-800 13:26共9个 783次 13次 0.15
9 5月3号 16:29-16:44 800-1200 16:30共58个 1054次 17.5次 0.96
10 5月5号 13:20-13:35 900-1500 13:22共325个 1182次 19.7次 5.4
11 5月5号 13:26-13:41 500-1500 13:30共23个 1180次 19.6次 0.38
12 5月6号 14:48-15:03 300-1800 15:00共26个 1020次 17次 0.43
13 5月6号 15:11-15:26 300-1800 15:24共116个 1489次 24次 1.9
14 5月6号 18:04-18:19 600-1000 15:24共12个 1645次 27次 0.2
15 5月6号 18:28-18:43 600-900 18:30共36个 888次 14.8次 0.6
16 5月6号 21:04-21:19 600-1000 21:08共5个 780次 13次 0.08
17 5月6号 21:36-21:51 500-650 共0个 842次 14次 0
18 5月7号 11:17-11:32 1200-1800 11:30共43个 1529次 25次 0.71
19 5月7号 13:49-14:04 1000-1200 14:00共15个 1067次 17.8次 0.26
20 5月7号 15:05-15:20 800-1650 15:06共1个 1486次 24次 0.01
21 5月7号 18:18-18:33 600-1200 18:30共36个 617次 10.2次 0.6
22 5月8号 10:50-10:50 300-1300 11:00共50个 1109次 18.3次 0.83
23 5月8号 15:38-15:53 200-1000 15:52共47个 371次 6.18次 0.78
当前熔断参数配置:
降级触发熔断百分比:50%
熔断恢复时间:3000毫秒
窗口:1秒
最低请求量:10个
超时时间:1000毫秒
当前配置参数下,发生熔断的条件为:一秒钟内,有十个以上的请求量,并且其中有50%以上的请求发生了降级(超时,异常等),则会触发打开熔断,然后后面五秒钟所有请求都会执行降级方法,五秒后如果有一个请求成功,那么熔断器会关闭,否则继续保持打开。
从以上统计的调用量和异常量来看,基本上都能满足在1秒的窗口期里面,有十个以上的请求,但是失败率超过50%的只有序号10一次,所以一般情况的调用并不会过于频繁的开启熔断。
阿波罗中的参数配置:
熔断器相关:
是否开启超时熔断:
mainSearch.hystrix.breakerTimeoutEnabled:true
超时时间(毫秒):
mainSearch.hystrix.timeoutInMilliseconds:1000
滑动窗口中,最少有多少个请求,才可能触发断路:
mainSearch.hystrix.breakerRequestNumber:10
设置断路器是否生效 :
mainSearch.hystrix.breakerEnabled:true
设置统计的滚动窗口的时间段大小(毫秒):
mainSearch.threadPool.metricsRollingStatisticalWindow:1000
主搜集团中台熔断百分比:
mainSearch.hystrix.thresholdPercentage: 50
主搜集团中台接口熔断恢复时间(毫秒):
mainSearch.hystrix.resume.mills:3000
接口线程池相关:
设置coreSize小于maximumSize的线程池能够支持maximumSize的并发数:
mainSearch.threadPool.allowMaximumSizeToDivergeFromCoreSize:true
线程池非核心线程活跃时间:
mainSearch.threadPool.keepAliveTimeMinutes:5
线程池最大线程数:
mainSearch.threadPool.maximumSize:20
线程池任务队列大小:
mainSearch.threadPool.maxQueueSize:200
线程池队列拒绝的阈值:
mainSearch.threadPool.queueSizeRejectionThreshold:5
十.参考文献:
https://github.com/Netflix/Hystrix/wiki
https://www.cnblogs.com/cnwuhao/p/10699841.html
https://www.jianshu.com/p/b9af028efebb
https://blog.youkuaiyun.com/Ramboll/article/details/84876403
https://juejin.cn/post/6880465020216410125
https://blog.youkuaiyun.com/m0_66782750/article/details/124054715