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.circuitBreakerhystrix.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 可视化监控使用

SpringCloud H版 GateWay 网关路由讲解

Spring Cloud构建微服务架构 服务容错保护(Hystrix断路器)Dalston版

微服务架构整理-(十SpringCloud实战之Hystrix [3])

微服务架构整理-(十SpringCloud实战之Hystrix [3])

微服务架构整理-(十SpringCloud实战之Hystrix [3])