11、SpringCloud 实战教程 - 集成Hystrix之服务熔断

什么是断路器?

“断路器”本身是一种开关装置,当某个服务单元发生故障监控(类似熔断保险丝),向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延。乃至雪崩。

什么是熔断机制?

熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某一个微服务出错不可用或者响应时间太长,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息,当检测到该节点微服务调用响应正常后,恢复调用链路。

在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务调用的状况,当失败的调用到一定的阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。

一、代码实现实例

修改我们上一篇文章新建的生产者cloud-provider-hystrix-payment8001的服务中的业务类PaymentService,这个paymentCircuitBreaker方法中的逻辑是传递一个参数id,当id<0时会抛出一个运行时的异常,否则就是满足条件,打印一个uuid。具体进行配置。如下图:

@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
    })
    public String paymentCircuitBreaker( Integer id)
    {
        if(id < 0)
        {
            throw new RuntimeException("******id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();
        return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
    }
//兜底降级的方法

    public String paymentCircuitBreaker_fallback(Integer id)
    {
        return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " +id;
    }

Hystrix断路器使用时最常用的三个重要指标参数

在微服务中使用Hystrix 作为断路器时,通常涉及到以下三个重要的指标参数(这里是写在@HystrixProperties注解中,当然实际项目中可以全局配置在yml或properties中)

1、 circuitBreaker.sleepWindowInMilliseconds;

断路器的快照时间窗,也叫做窗口期。可以理解为一个触发断路器的周期时间值,默认为10秒(10000)

2、 circuitBreaker.requestVolumeThreshold;

断路器的窗口期内触发断路的请求阈值,默认为20。换句话说,假如某个窗口期内的请求总数都不到该配置值,那么断路器连发生的资格都没有。断路器在该窗口期内将不会被打开。

3、 circuitBreaker.errorThresholdPercentage;

断路器的窗口期内能够容忍的错误百分比阈值,默认为50(也就是说默认容忍50%的错误率) 。打个比方,假如一个窗口期内,发生了100次服务请求,其中50次出现了错误。在这样的情况下,断路器将会被打开。在该窗口期结束之前,即使第51次请求没有发生异常,也将被执行fallback逻辑。


综上所述,在以上三个参数缺省的情况下,Hystrix断路器触发的默认策略为:

在10秒内,发生20次以上的请求时,假如错误率达到50%以上,则断路器将被打开。(当一个窗口期过去的时候,断路器将变成半开(HALF-OPEN)状态,如果这时候发生的请求正常,则关闭,否则又打开)

然后在控制层进行调用,如下图:

 @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id)
    {
        String result = paymentService.paymentCircuitBreaker(id);
        log.info("****result: "+result);
        return result;
    }

启动Eureka7001服务和该8001服务,然后访问http://localhost:8001/payment/circuit/1,传递的参数id是1,大于0,所以会返回流水号uuid。如下图:

 

然后我们将参数id传递一个负数的,进行访问http://localhost:8001/payment/circuit/-1, 就会返回运行时的异常的提示。如下图:

 

接下来我们就进行测试,由于上边的配置是在10秒内,访问10次,如果超过6次错误,就会打开我们的断路器,打开断路器就是我们传递正确的参数,也不成功返回流水号uuid。如下图:

 

这就说明断路器打开了,然后随着时间增加和我们正确的访问,容错率变低,就慢慢恢复了,断路器的状态变为了半开状态,随着正确率的提高,会自动关闭。就可以正确访问了, 如下图:

 

熔断三大类型:

熔断打开:请求不再调用当前服务,内部设置一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态。

熔断关闭:熔断关闭不会对服务进行熔断。

熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则认为当前服务恢复正常,关闭熔断。

 

断路器开启或者关闭的条件有哪些?

1、 当满足一定的阈值的时候(默认10秒钟超过20个请求次数);
2、 当失败率达到一定的时候(默认10秒钟超过50%的请求次数);
3、 当达到以上阈值,断路器将会开启;
4、 当开启的时候,所有请求都不会进行转发;
5、 一段时间之后(默认5秒),这个时候断路器是半开状态,会让其他一个请求进行转发;
6、 如果成功,断路器会关闭,若失败,继续开启重复第4步和第5步;

断路器打开之后会发生什么?

1、 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback,通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果;
2、 原来的主逻辑要如何恢复呢?;
对于这一问题,Hystrix也为我们实现了自动恢复功能,当断路器打开,对主逻辑进行熔断之后,Hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑。当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。

Hystrix工作流程

 

官网的的流程图描述了Hystrix的工作流程:
1、 每次调用都会创建一个HystrixCommand;
2、 执行execute或queue做同步\异步调用;
3、 判断熔断器是否打开,如果打开跳到步骤8,否则进入步骤4;
4、 判断线程池/信号量是否跑满,如果跑满进入步骤8,否则进入步骤5;
5、 调用HystrixCommand的run方法,如果调用超时进入步骤8;
6、 判断是否调用成功,返回成功调用结果,如果失败进入步骤8;
7、 计算熔断器状态,所有的运行状态(成功,失败,拒绝,超时)上报给熔断器,用于统计从而判断熔断器状态;
8、 降级处理逻辑,根据上方的步骤可以得出以下四种情况会进入降级处理:熔断器打开、线程池/信号量跑满、调用超时、调用失、;
9、 返回执行成功结果;