SpringCloud H版 Hystrix 服务熔断限流讲解
Posted 小毕超
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud H版 Hystrix 服务熔断限流讲解相关的知识,希望对你有一定的参考价值。
一、Hystrix
上一篇文章中我们对Hystrix 进行了介绍,及讲解了Hystrix 的服务降级的使用,本篇我们继续讲解Hystrix 的服务熔断限流。
上篇博客地址:https://blog.csdn.net/qq_43692950/article/details/121996806
本篇我们主要讲解Hystrix 的服务熔断,什么是服务熔断呢?服务熔断可以理解为家里面的保险丝,当某个电器出现故障,造成短路,随即保险丝断掉,以保护家里面的其他电器的安全。在微服务中也是如此,一旦某个接口出现问题,大量请求调用该接口做无效的等待,占用了大量的线程,从而导致正常的接口无法调用,出现了服务雪崩的问题。
熔断机制则是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
在Hystrix中。Hystrix会监控微服务间调用的状况,当在一定时间内,达到了指定的请求数量,其中失败率达到一个阈值,便任务该接口不可用,后续对该接口的请求,会直接进入降级处理。缺省是5秒内20次调用失败,就会启动熔断机制。
二、Hystrix 服务熔断
上面我们了解了服务熔断的场景,现在我们开始实操一下,我们在上篇博客的基础上,修改配置文件
# Hystrix
hystrix:
command:
default:
execution:
timeout:
enabled: true #是否应该有超时
isolation: # 隔离策略
#隔离策略,有THREAD 、SEMAPHORE,
#THREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制
#SEMAPHORE - 它在调用线程上执行,并发请求受到信号量计数的限制
strategy: THREAD
semaphore: #信号量
maxConcurrentRequests: 100 #配置信号量的大小,当最大并发请求数达到该设置值,后续的请求将会被拒绝
thread:
timeoutInMilliseconds: 2000 #服务调用超时时间,THREAD隔离模式下是请求超时是会取消调用线程从而立即返回的,SEMAPHORE模式下会等待响应回来再判断是否超时。
interruptOnTimeout: true #执行超时的时候,是否需要将他中断
interruptOnCancel: true #是否在方法执行被取消时中断方法
circuitBreaker: #服务熔断策略
enabled: true #确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求,默认值 true
requestVolumeThreshold: 10 #用来设置在滚动时间窗中,断路器的最小请求数。例如:默认值 20 的情况下,如果滚动时间窗(默认值 10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
errorThresholdPercentage: 80 #用来设置断路器打开的错误百分比条件。例如,默认值为 50 的情况下,表示在滚动时间窗中,在请求数量超过 circuitBreaker.requestVolumeThreshold 阈值的请求下,如果错误请求数的百分比超过50,就把断路器设置为"打开"状态,否则就设置为"关闭"状态。
sleepWindowInMilliseconds: 5000 #用来设置当断路器打开之后的休眠时间窗。默认值 5000 毫秒,休眠时间窗结束之后,会将断路器设置为"半开"状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为"打开"状态,如果成功就设置为"关闭"状态。
metrics: #统计器
rollingStats: #滚动时间窗口
timeInMilliseconds: 10000#用于设置滚动时间窗的长度,单位毫秒,该时间用于断路器判断健康度时需要收集信息的持续时间,默认值 10000 。断路器值啊收集指标信息时候会根据设置的时间窗长度拆分成多个"桶"来累计各度量值,每个"桶"记录了一段时间内的采集指标。
numBuckets: 10 #用来设置滚动时间窗统计指标信息时,划分"桶"的数量,默认值 10 。 metrics.rollingStats.timeInMilliseconds 参数的设置必须能被该参数整除,否则将抛出异常。
上面配置较多,我也写了详细的注释,我们主要关注 hystrix.command.default.circuitBreaker
和 hystrix.command.default.metrics
下的参数,其中 metrics.rollingStats.timeInMilliseconds
表示滑动窗口的大小,所有的计算都是在这个时间范围内的。circuitBreaker.requestVolumeThreshold
表示滑动窗口时间范围内,请求的数量达到该值,才会判断错误百分比,如果请求数量没有超过该值,就算全部失败也不会进入熔断状态。circuitBreaker.errorThresholdPercentage
就是用来指定百分比的占比了,如果指定50 ,则表示一半的请求失败,该接口就会进入熔断状态。circuitBreaker.sleepWindowInMilliseconds
则表示进入熔断状态多长时间释放一次真实的请求,用来探测接口是否以恢复正常,如果可以请求成功,则关闭熔断器,否则的话该接口依然为熔断状态。
从上面的配置文件中,我们的规则为,在10秒的时间内,请求数量达到了10次,并且有80%的请求失败,则将该接口进入熔断状态,如果进入熔断状态,则5秒放行一条请求,用来探测接口是否正常。
下面我们编写一个测试接口:
@Slf4j
@RestController
@RequestMapping("/circuitBreaker")
public class TestCircuitBreakerController
AtomicInteger integer = new AtomicInteger(0);
@HystrixCommand(fallbackMethod = "fallback")
@GetMapping("/getTest")
public ResponseTemplate getTest() throws InterruptedException
integer.incrementAndGet();
log.info("接口请求,请求次数,,当前时间: ", integer.get(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
int a = 1 / 0;
return ResSuccessTemplate.builder().build();
public ResponseTemplate fallback()
return ResFailTemplate.builder().message("服务降级!").build();
在该接口中我们对接口的调用次数进行了计算,并记录当前的时间,如果发生了熔断就不会进入我们这个方法,测试后应该得到结果为,调用次数,远大于该接口的触发数。
下面我们可以编写一个测试类:
@Slf4j
public class HystrixTest
public static void main(String[] args) throws InterruptedException
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/circuitBreaker/getTest";
for (int i = 1; i <= 100; i++)
String forObject = restTemplate.getForObject(url, String.class);
Thread.sleep(300);
log.info("调用次数: ,返回结果:", i, forObject);
注意这里最好做个延时,因为我们滑动窗口时间为10秒,循环100次不加延时1秒多就请求完了,而滑动窗口10秒才去做请求次数和成功率的判断,所以有可能达不到理想的效果。
当100次请求完毕后:
我们看下接口这边打印的日志:
只有15次请求进入了该方法,前面10次是最开始进入的请求,检测出超出了80%的错误,则该接口即进入了熔断状态,然后每5秒释放一个请求来判断接口是否正常。
三、Hystrix 服务限流
我们都知道每个服务都有一个请求上限,达到该上限后,对我们服务的安全肯定会造成一些影响,比如内存溢出、请求缓慢等,因此我们应该限制服务的请求数,来保证服务的正常工作,因此 Hystrix 可以根据信号量和线程池的大小进行限流降级,比如我们在上面的基础上修改配置文件:
使用线程池的方式,实现限流
hystrix:
command:
default:
execution:
timeout:
enabled: false #是否应该有超时
isolation: # 隔离策略
#隔离策略,有THREAD 、SEMAPHORE,
#THREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制
#SEMAPHORE - 它在调用线程上执行,并发请求受到信号量计数的限制
strategy: THREAD
semaphore: #信号量
maxConcurrentRequests: 100 #配置信号量的大小,当最大并发请求数达到该设置值,后续的请求将会被拒绝
thread:
timeoutInMilliseconds: 2000 #服务调用超时时间,THREAD隔离模式下是请求超时是会取消调用线程从而立即返回的,SEMAPHORE模式下会等待响应回来再判断是否超时。
interruptOnTimeout: true #执行超时的时候,是否需要将他中断
interruptOnCancel: true #是否在方法执行被取消时中断方法
circuitBreaker: #服务熔断策略
enabled: false #确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求,默认值 true
requestVolumeThreshold: 10 #用来设置在滚动时间窗中,断路器的最小请求数。例如:默认值 20 的情况下,如果滚动时间窗(默认值 10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
errorThresholdPercentage: 80 #用来设置断路器打开的错误百分比条件。例如,默认值为 50 的情况下,表示在滚动时间窗中,在请求数量超过 circuitBreaker.requestVolumeThreshold 阈值的请求下,如果错误请求数的百分比超过50,就把断路器设置为"打开"状态,否则就设置为"关闭"状态。
sleepWindowInMilliseconds: 5000 #用来设置当断路器打开之后的休眠时间窗。默认值 5000 毫秒,休眠时间窗结束之后,会将断路器设置为"半开"状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为"打开"状态,如果成功就设置为"关闭"状态。
metrics: #统计器
rollingStats: #滚动时间窗口
timeInMilliseconds: 10000 #用于设置滚动时间窗的长度,单位毫秒,该时间用于断路器判断健康度时需要收集信息的持续时间,默认值 10000 。断路器值啊收集指标信息时候会根据设置的时间窗长度拆分成多个"桶"来累计各度量值,每个"桶"记录了一段时间内的采集指标。
numBuckets: 10 #用来设置滚动时间窗统计指标信息时,划分"桶"的数量,默认值 10 。 metrics.rollingStats.timeInMilliseconds 参数的设置必须能被该参数整除,否则将抛出异常。
threadpool: #线程池策略
default:
coreSize: 5 #设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量,默认值 10
allowMaximumSizeToDivergeFromCoreSize: true #是否允许线程池扩展到最大线程池数量,默认为 false;
maximumSize: 10 #线程池中线程的最大数量,默认值是 10,此配置项单独配置时并不会生效,需要启用 allowMaximumSizeToDivergeFromCoreSize 项。
maxQueueSize: 10 #设置线程池的最大队列大小,当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,否则使用 LinkedBlockingQueue 实现的队列,默认值 -1
queueSizeRejectionThreshold : 20 #用来为队列设置拒绝阈值,即使队列没有到达最大值也能拒绝请求,该属性主要对 LinkedBlockingQueue 队列的补充,默认值 5,当 maxQueueSize 属性为 -1 时候,该属性无效
keepAliveTimeMinutes: 2 #等到线程池空闲后,多于核心数量的线程还会被回收,此值指定了线程被回收前的存活时间,默认为 2,即两分钟。
我们把超时时间和熔断开发关了,以防止影响我们测试这部分的功能,添加了线程池的配置,主要配置了核心线程数,最大线程数,及队列的大小,当队列满时,并且已经使用了最大线程数,后面的请求会直接降级,以保护服务的安全,注意这里既然要根据线程池了,所及隔离策略必须要设为线程池隔离 THREAD
编写测试接口:
@Slf4j
@RestController
@RequestMapping("/limit")
public class TestLimitController
AtomicInteger integer = new AtomicInteger(0);
@HystrixCommand(fallbackMethod = "fallback")
@GetMapping("/getTest")
public ResponseTemplate getTest() throws InterruptedException
integer.incrementAndGet();
log.info("接口请求,请求次数,,当前时间: ", integer.get(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
TimeUnit.SECONDS.sleep(2);
return ResSuccessTemplate.builder().build();
public ResponseTemplate fallback()
return ResFailTemplate.builder().message("服务降级!").build();
修改测试程序:
@Slf4j
public class HystrixTest
public static void main(String[] args) throws InterruptedException
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/limit/getTest";
for (int i = 1; i <= 100; i++)
int finalI = i;
new Thread(()->
String forObject = restTemplate.getForObject(url, String.class);
try
Thread.sleep(300);
catch (InterruptedException e)
e.printStackTrace();
log.info("调用次数: ,返回结果:", finalI, forObject);
).start();
这里修改为并行请求,注意,这里为了方便直接new的线程,但这不是一个好习惯,大家还是要使用线程池呀!
开始测试:
可以看到部分的请求拿到的都是降级返回,我们看下接口的打印情况:
只有20 个请求被释放进来,其余均被限流降级处理!
使用信号量的方式,实现限流
信号量的方式则表示,系统能够承受的最大量,超过该值,其后的均被降级处理,下面再次修改我们的配置文件:
hystrix:
command:
default:
execution:
timeout:
enabled: false #是否应该有超时
isolation: # 隔离策略
#隔离策略,有THREAD 、SEMAPHORE,
#THREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制
#SEMAPHORE - 它在调用线程上执行,并发请求受到信号量计数的限制
strategy: SEMAPHORE
semaphore: #信号量
maxConcurrentRequests: 50 #配置信号量的大小,当最大并发请求数达到该设置值,后续的请求将会被拒绝
thread:
timeoutInMilliseconds: 2000 #服务调用超时时间,THREAD隔离模式下是请求超时是会取消调用线程从而立即返回的,SEMAPHORE模式下会等待响应回来再判断是否超时。
interruptOnTimeout: true #执行超时的时候,是否需要将他中断
interruptOnCancel: true #是否在方法执行被取消时中断方法
circuitBreaker: #服务熔断策略
enabled: false #确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求,默认值 true
requestVolumeThreshold: 10 #用来设置在滚动时间窗中,断路器的最小请求数。例如:默认值 20 的情况下,如果滚动时间窗(默认值 10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
errorThresholdPercentage: 80 #用来设置断路器打开的错误百分比条件。例如,默认值为 50 的情况下,表示在滚动时间窗中,在请求数量超过 circuitBreaker.requestVolumeThreshold 阈值的请求下,如果错误请求数的百分比超过50,就把断路器设置为"打开"状态,否则就设置为"关闭"状态。
sleepWindowInMilliseconds: 5000 #用来设置当断路器打开之后的休眠时间窗。默认值 5000 毫秒,休眠时间窗结束之后,会将断路器设置为"半开"状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为"打开"状态,如果成功就设置为"关闭"状态。
metrics: #统计器
rollingStats: #滚动时间窗口
timeInMilliseconds: 10000 #用于设置滚动时间窗的长度,单位毫秒,该时间用于断路器判断健康度时需要收集信息的持续时间,默认值 10000 。断路器值啊收集指标信息时候会根据设置的时间窗长度拆分成多个"桶"来累计各度量值,每个"桶"记录了一段时间内的采集指标。
numBuckets: 10 #用来设置滚动时间窗统计指标信息时,划分"桶"的数量,默认值 10 。 metrics.rollingStats.timeInMilliseconds 参数的设置必须能被该参数整除,否则将抛出异常。
我们将隔离策略修改为了信号量SEMAPHORE,并设置信号量大小为50,这意味这,服务同一时间并发只能达到50的并发量,我们继续使用上面的测试程序进行测试:
也是有一部分降级有一部分成功,我们看下接口的日志信息:
可以看到只有50个请求进来了。
四、Hystrix 的常用配置及注释
以下是我总结的常用的配置项,有兴趣可以参考。
# Hystrix
hystrix:
command:
default:
execution:
timeout:
enabled: true #是否应该有超时
isolation: # 隔离策略
#隔离策略,有THREAD 、SEMAPHORE,
#THREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制
#SEMAPHORE - 它在调用线程上执行,并发请求受到信号量计数的限制
strategy: THREAD
semaphore: #信号量
maxConcurrentRequests: 100 #配置信号量的大小,当最大并发请求数达到该设置值,后续的请求将会被拒绝
thread:
timeoutInMilliseconds: 30000 #服务调用超时时间,THREAD隔离模式下是请求超时是会取消调用线程从而立即返回的,SEMAPHORE模式下会等待响应回来再判断是否超时。
interruptOnTimeout: true #执行超时的时候,是否需要将他中断
interruptOnCancel: true #是否在方法执行被取消时中断方法
fallback: # 服务降级策略
enabled: true #服务降级策略是否启用,默认值 true ,如果设置为false,当请求失败或拒绝发生时,将不会调用 HystrixCommand.getFallback() 来执行服务降级逻辑
isolation:
semaphore:
maxConcurrentRequests: 10000 # 设置从调用线程中允许HystrixCommand.getFallback()方法执行的最大并发请求数。当达到最大并发请求时,后续的请求将会被拒绝并抛出异常。
circuitBreaker: #服务熔断策略
enabled: true #确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求,默认值 true
requestVolumeThreshold: 10 #用来设置在滚动时间窗中,断路器的最小请求数。例如:默认值 20 的情况下,如果滚动时间窗(默认值 10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
errorThresholdPercentage: 80 #用来设置断路器打开的错误百分比条件。例如,默认值为 50 的情况下,表示在滚动时间窗中,在请求数量超过 circuitBreaker.requestVolumeThreshold 阈值的请求下,如果错误请求数的百分比超过50,就把断路器设置为"打开"状态,否则就设置为"关闭"状态。
sleepWindowInMilliseconds: 5000 #用来设置当断路器打开之后的休眠时间窗。默认值 5000 毫秒,休眠时间窗结束之后,会将断路器设置为"半开"状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为"打开"状态,如果成功就设置为"关闭"状态。
metrics: #统计器
rollingStats: #滚动时间窗口
timeInMilliseconds: 10000 #用于设置滚动时间窗的长度,单位毫秒,该时间用于断路器判断健康度时需要收集信息的持续时间,默认值 10000 。断路器值啊收集指标信息时候会根据设置的时间窗长度拆分成多个"桶"来累计各度量值,每个"桶"记录了一段时间内的采集指标。
numBuckets: 10 #用来设置滚动时间窗统计指标信息时,划分"桶"的数量,默认值 10 。 metrics.rollingStats.timeInMilliseconds 参数的设置必须能被该参数整除,否则将抛出异常。
rollingPercentile: #百分位数
enabled: true #用来设置对命令执行的延迟是否使用百分位数来跟踪和计算,默认值 true ,如果设置为 false 那么所有概要统计都将返回 -1
timeInMilliseconds: 60000 #用来设置百分位统计的滚动窗口的持续时间,单位:毫秒,默认值 60000
numBuckets: 6 # 用来设置百分位统计窗口中使用"桶"的数量,默认值 6
bucketSize: 100 #用来设置在执行过程中每个"桶"中保留的最大执行次数,如果在滚动时间窗内发生超该设定值的执行次数,就从最初的位置开始重写,例如:设置为 100,滚动窗口为 10 秒,若在10秒内一个"桶"中发生了500次执行,那么该"桶"中只保留最后的100次执行的统计,默认值 100
healthSnapshot:
intervalInMilliseconds: 500 # 用来设置采集影响断路器状态的健康快照(请求的成功、错误百分比)的间隔等待时间,默认值 500
requestCache:
enabled: false #是否开启请求缓存
requestLog:
enabledg: true #用来设置 HystrixCommand 的执行和事件是否打印日志到 HystrixRequestLog 中,默认值 true
#自己的key
userGetKey:
execution:
timeout:
enabled: false #是否应该有超时
isolation:
#隔离策略,有THREAD 、SEMAPHORE,
#THREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制
#SEMAPHORE - 它在调用线程上执行,并发请求受到信号量计数的限制
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 2000 #服务调用超时时间
interruptOnTimeout: true
threadpool: #线程池策略
default:
coreSize: 100 #设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量,默认值 10
allowMaximumSizeToDivergeFromCoreSize: true #是否允许线程池扩展到最大线程池数量,默认为 false;
maximumSize: 1000 #线程池中线程的最大数量,默认值是 10,此配置项单独配置时并不会生效,需要启用 allowMaximumSizeToDivergeFromCoreSize 项。
maxQueueSize: 8000 #设置线程池的最大队列大小,当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,否则使用 LinkedBlockingQueue 实现的队列,默认值 -1
queueSizeRejectionThreshold : 8000 #用来为队列设置拒绝阈值,即使队列没有到达最大值也能拒绝请求,该属性主要对 LinkedBlockingQueue 队列的补充,默认值 5,当 maxQueueSize 属性为 -1 时候,该属性无效
keepAliveTimeMinutes: 2 #等到线程池空闲后,多于核心数量的线程还会被回收,此值指定了线程被回收前的存活时间,默认为 2,即两分钟。
metrics:
rollingPercentile:
timeInMilliseconds: 10000 #用来设置线程池统计的滚动窗口的持续时间,单位:毫秒,默认值 10000
numBuckets: 10 #用来设置线程池统计窗口中使用"桶"的数量,默认值 10
collapser:
default:
maxRequestsInBatch: 100 # 用来设置一次请求合并批处理允许的最大请求数量,默认值 Integer.MAX_VALUE
timerDelayInMilliseconds: 10 #用来设置批处理过程中每个命令延迟的时间,单位毫秒,默认值 10
requestCache:
enabled: true #用来设置批处理过程中是否开启请求缓存,默认值 true
喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!
以上是关于SpringCloud H版 Hystrix 服务熔断限流讲解的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud H版 Hystrix dashboard 可视化监控使用
Spring Cloud构建微服务架构 服务容错保护(Hystrix断路器)Dalston版
微服务架构整理-(十SpringCloud实战之Hystrix [3])