Spring Cloud中断路器的使用

在断路器对象中封装受保护的方法调用,该对象监控调用和断路情况, 调用失败次数触发阈值后,后续 调用直接由断路器返回错误,不再执行实际调用 。默认情况下,抛出的任何异常都计算率,可以进行配置。
CircuitBreaker 有三种状态: Closed( 闭合 ) Open( 打开 ) HalfOpen( 半开 ) Closed 状态下它是闭合的,如果下游服务调用失败超过一定数量,它将变为Open 状态。当在 Open 状态下经过一段后,进入HalfOpen 状态。如果在 HalfOpen 状态下调用的失败超过阀值,则返回到 Open 状态,否则切换到Closed状态。

1. Maven导包

<!-- 熔断:resilience4j -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

2. 配置文件

Resilience 4j 是没有必须的配置项的
###Resilience 4j
##打开Resilience4j报告熔断器和限速器监控指标的Actuator端点,默认是禁用的
management.health.circuitbreakers.enabled=true
management.health.ratelimiters.enabled=true
### 断路器的配置
## 统计窗口,基于时间则创建对应长度的一个数组,如:10,则通过一个长度为10的数组记录每一秒的请求
数和失败数,然后循环写入记录的数据,基于数量统计时原理类似
# 配置是基于时间段统计,还是基于请求数统计,默认COUNT_BASED
resilience4j.circuitbreaker.configs.default.slidingWindowType=COUNT_BASED
# 如果是基于请求数统计,则表示请求的数量,如果是基于时间段统计,表示统计的秒数,默认100
resilience4j.circuitbreaker.configs.default.slidingWindowSize=3
##当慢调用比率超过阀值,则断路器打开
# 慢调用比率阀值,默认100%
resilience4j.circuitbreaker.configs.default.slowCallRateThreshold=100
# 响应时间超过以下值即视为慢调用,默认60000毫秒
resilience4j.circuitbreaker.configs.default.slowCallDurationThreshold=20000
## 状态维护
#故障率阀值,默认50;也就是统计范围内的请求失败率超过这个阀值,断路器进入Open状态
resilience4j.circuitbreaker.configs.default.failureRateThreshold=50
#在计算故障率之前所需的最小调用次数,不达到这个值,不计算故障率。默认100
#注意:如果统计窗口配置为10,此处配置为1,则第一次请求失败时故障率就会统计,即100%,会导致断路
器打开
resilience4j.circuitbreaker.configs.default.minimum-number-of-calls=1
#Open状态持续时间,默认60000毫秒;也就是断路器断开这个毫秒数后,进入HalfOpen状态,可以尝试调
用
resilience4j.circuitbreaker.configs.default.waitDurationInOpenState=10000000
#HalfOpen状态时允许的呼叫数,默认10;也就是在HalfOpen状态下,可以发送这么多个请求,如果失败率
还是
#高于故障率阀值,则返回到Open状态,否则切换到Closed状态
resilience4j.circuitbreaker.configs.default.permittedNumberOfCallsInHalfOpenState
=10
## 注册Health信息
resilience4j.circuitbreaker.configs.default.registerHealthIndicator=true
###熔断器实例配置,也就是配置单个实例
#backendA为实例名称,其他属性也可以配置,此处只是个例子
resilience4j.circuitbreaker.instances.backendA.registerHealthIndicator=true

3. 创建断路器

3.1 通过API的方式使用断路器

@Autowired    //自动注入CircuitBreakerFactory 
private CircuitBreakerFactory circuitBreakerFactory;

public Sample get(long id){
    
    //通过circuitBreakerFactory 创建Resilience4J CircuitBreaker实例

    CircuitBreaker circuitBreaker = circuitBreakerFactory.create("get");

     //通过run方法传入需要执行的方法和对应的降级方法,CircuitBreaker会捕获第一个参数的get方法抛出异常,然后降级处理,并且统计故障率
    return circuitBreaker.run(() -> getRequest(id), throwable ->reliable(id,throwable));
}

/**
* 发送请求的方法
*/
private Sample getRequest(long id) {
//通过RestTemplate发送请求,状态码为4**、5**的响应抛出异常
}


/**
* 断路器的回调方法,断路器进行服务降级时调用。参数和返回值要和服务方法一致,最后需要加一 * 个Throwable参数
*/

public Sample reliable(long id, Throwable e) {
    logger.info("服务降级..............Throwable..............");
    //断路器打开抛出的异常
    if (throwable instanceof CallNotPermittedException) {
        CallNotPermittedException exception = (CallNotPermittedException) throwable;}
    //RestTemplate的请求方法中收到了状态码为4**、5**的响应抛出的异常
    if (throwable instanceof HttpStatusCodeException) {
        HttpStatusCodeException exception = (HttpStatusCodeException) throwable;
    }
    return new Sample(id, "服务降级,原因:" + e.getMessage());
}

3.2 通过注解方式使用熔断器

@Service
public class SampleService implements ISampleService {
    private static final Logger logger = LoggerFactory.getLogger(SampleService.class);
    @Autowired
    private RestTemplate restTemplate;

    @CircuitBreaker(name = "get", fallbackMethod = "reliable")
    public Sample get(long id) {
    }
    /* 断路器的回调方法,断路器进行服务降级时调用。参数和返回值要和服务方法一致,需要加一个CallNotPermittedException参数,这个只针对熔断进行降级。返回4*、5*响应码的异常由
Retry重试后降级。这个异常如果需要被其它处理器感知到,则可以继续抛出 */


    public Sample reliable(long id, CallNotPermittedException e) {
        logger.info("服务降级..............Throwable..............");
        logger.error("", e);
        return new Sample(id, "服务降级,原因:" + e.getMessage());
    }
}

 重点注意

1. 如果要在get()方法上加 @Transactional 注解开启get方法的事务管理的话 ,需要将get方法中的调用下游服务器的代码,拆出来并在其声明前加熔断器的注解。

原因:注解事务和熔断器是通过JDK的动态代理实现的,熔断器的代理要在事务代理之前,会导致事务中的异常也会触发熔断器的统计,而熔断器是用于检测应用调用的下游服务所抛出的异常的,所以不合理;

2. 在get()方法中不能直接调用熔断器方法,因为调用时默认是this.调用,非代理对象,与实际目的不符合,也就是熔断器注解被跳过,没有作用(没用调用代理对象,反而调用了被代理对象)。

解决办法:JDK的动态代理的代理对象,会存储在AopContext中,但是从中取代理对象需要修改Spring AOP配置 

        @EnableAspectJAutoProxy(exposeProxy = true) 

    @Override
    @Transactional
    public Orders save(Orders orders){
        Orders orders1 = orderDao.save(orders);

        OrderService orderService = (OrderService) AopContext.currentProxy();
        int num = orderService.discountStocks(orders);

        logger.warn("扣减结果:{}",num);
        if(num==0){
            throw new StockShortException("库存不足");
        }

        return orders1;
    }


    @Override
    @CircuitBreaker(name = "save",fallbackMethod = "fallbackMethod")
    public int discountStocks(Orders orders){
        logger.warn("orders:{}", orders);
        RequestEntity<Products> requestEntity = RequestEntity.put("http://product-service/api/v1/products/" + orders.getProductId())
                .contentType(MediaType.APPLICATION_JSON)
                .body(new Products(orders.getNum()));

        try {
            logger.warn("try");
            return restTemplate.exchange(requestEntity, Integer.class).getBody();
        } catch (HttpServerErrorException ex) {
            throw new ProductServerException("下游服务异常", ex);
        }

    }

    public int fallbackMethod(Orders orders, CallNotPermittedException ex){
        logger.warn("服务降级:{}", orders.getProductId());
        throw new ProductServerException("断路器处于打开状态", ex);

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值