SpringCloud系列(34)--使用Hystrix进行服务熔断
前言:在上一节中我们使服务降级的相关代码与业务代码解耦了,使得服务降级的相关配置变得更明了清晰,而关于服务降级的内容也介绍完毕,所以本节内容则是关于如何使用Hystrix进行服务熔断处理。
1、熔断机制概述
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路,在SpringCloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。(熔断机制的注解是@HystrixCommand)
一篇关于服务熔断的Martin Fowler大神写的论文,感兴趣的同学可以详细看下:
https://martinfowler.com/bliki/CircuitBreaker.html
我这里截取了论文里的一张图,图里有3种熔断状态:
(1)Closed(熔断关闭):熔断关闭后不会再对服务进行熔断
(2)Open(熔断打开):请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态
(3)Half Open(熔断半开):部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
由图可知服务异常后,熔断器会进入Open状态,后续所有的请求都不会被转发,所有的请求都会被进行服务降级处理,等过了一段时间后熔断器才会从Open状态到HalfOpen状态,这时候会让其中一个请求进行转发,如果转发成功,断路器进入Closed状态,如果失败则继续Open的状态
2、修改cloud-provider-hystrix-payment8001子模块的PaymentServiceImpl类
注:@HystrixProperty注解的更多key/value参数设置可以查看HystrixCommandProperties 类
package com.ken.springcloud.service.impl;import cn.hutool.core.util.IdUtil;
import com.ken.springcloud.service.PaymentService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;import java.util.concurrent.TimeUnit;@Service
public class PaymentServiceImpl implements PaymentService {@Overridepublic String paymentInfoOK(Integer id) {return "线程池:" + Thread.currentThread().getName() + "paymentInfoOK,id: " + id;}@Override//一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法,这里设置服务降级的条件为连接超时超过3秒,即3秒内走paymentInfoTimeOut方法内的业务逻辑,超过3秒走paymentInfoTimeOutHandler方法@HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandler",commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")})public String paymentInfoTimeOut(Integer id) {int timeNumber = 2;try {TimeUnit.SECONDS.sleep(timeNumber);} catch (InterruptedException e) {e.printStackTrace();}return "线程池:" + Thread.currentThread().getName() + "paymentInfoTimeOut,id: " + id + "耗时" + timeNumber + "秒";}public String paymentInfoTimeOutHandler(Integer id) {return "线程池:" + Thread.currentThread().getName() + "系统繁忙,请稍后再试";}@Override//下述设置的意思是启用断路器,然后在10000ms这个时间里10次请求有60%失败即6次失败的话,断路器将会起作用@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback",commandProperties = {//开启断路器@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//时间窗口期:断路器确定是否打开需要统计—些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//请求总数阀值:在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//错误百分比阈值:当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),})public String paymentCircuitBreaker(Integer id) {if(id < 0) {throw new RuntimeException("id不能为负数");}return "线程池:" + Thread.currentThread().getName() + "paymentCircuitBreaker,id: " + id;}public String paymentCircuitBreakerFallback(@PathVariable("id") Integer id) {return "id不能为负数,请重试";}}
3、修改cloud-provider-hystrix-payment8001子模块的PaymentController类
package com.ken.springcloud.controller;import com.ken.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@Slf4j
@RestController
public class PaymentController {@Resourceprivate PaymentService paymentService;@Value("${server.port}")private String serverPort;@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfoOK(@PathVariable("id") Integer id) {String result = paymentService.paymentInfoOK(id);log.info("******result:" + result);return result;}@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfoTimeOut(@PathVariable("id") Integer id) {String result = paymentService.paymentInfoTimeOut(id);log.info("******result:" + result);return result;}@GetMapping("/payment/circuit/{id}")public String paymentCircuitBreaker(@PathVariable("id") Integer id) {String result = paymentService.paymentCircuitBreaker(id);log.info("******result:" + result);return result;}}
4、重启cloud-provider-hystrix-payment8001服务
5、在浏览器的地址栏里输入http://localhost:8001/payment/circuit/1,通过调用这个接口查看接口是否正常运行
6、在浏览器的地址栏里输入http://localhost:8001/payment/circuit/-1,通过输入错误的参数查看接口的运行情况
通过输入错误参数然后调用这个接口可以看到程序异常然后走了服务降级,由paymentCircuitBreakerFallback方法进行兜底处理
7、接下来,我们给接口指定参数为负数,然后通过不断的调用接口,使接口不断的异常,然后再给接口指定参数为正数,看其运行情况
由动图可以看出,我不断地给接口传负数,然后接口一直在异常,在符合我们设置的条件下断路器打开了(即在10s内10个请求里有6个及以上的请求异常)一旦断路器打开,就算id非负也会调用服务降级的方法,在此时我们只能等待断路器自己从半开到关闭状态,此时接口才恢复正常服务