Resilience4j简介-circuitbreaker 异常熔断实践介绍

4.2 Resilience4j简介

4.2.1 Resilience4j简介

    Netflix的Hystrix微服务容错库已经停⽌更新,官⽅推荐使⽤Resilience4j代替Hystrix,或者使⽤Spring Cloud Alibaba的Sentinel组件。

    Resilience4j是受到Netflix Hystrix的启发,为Java8和函数式编程所设计的轻量级容错框架。整个框架只是使⽤了Varr的库,不需要引⼊其他的外部依赖。与此相⽐,Netflix Hystrix对Archaius具有编译依赖,⽽Archaius需要更多的外部依赖,例如Guava和Apache Commons Configuration。

    Resilience4j提供了提供了⼀组⾼阶函数(装饰器),包括断路器,限流器,重试机制,隔离机制。你可以使⽤其中的⼀个或多个装饰器对函数式接⼝,lambda表达式或⽅法引⽤进⾏装饰。这么做的优点是你可以选择所需要的装饰器进⾏装饰。

    在使⽤Resilience4j的过程中,不需要引⼊所有的依赖,只引⼊需要的依赖即可。

核⼼模块:

resilience4j-circuitbreaker: 熔断

resilience4j-ratelimiter: 限流

resilience4j-bulkhead: 隔离

resilience4j-retry: ⾃动重试

resilience4j-cache: 结果缓存

resilience4j-timelimiter: 超时处理

4.2.2 Resilience4j和Hystrix的异同

Hystrix使⽤HystrixCommand来调⽤外部的系统,⽽R4j提供了⼀些⾼阶函数,例如断路器、限流器、隔离机制等,这些函数作为装饰器对函数式接⼝、lambda表达式、函数引⽤进⾏装饰。此外,R4j库还提供了失败重试和缓存调⽤结果的装饰器。你可以在函数式接⼝、

lambda表达式、函数引⽤上叠加地使⽤⼀个或多个装饰器,这意味着隔离机制、限流器、重试机制等能够进⾏组合使⽤。这么做的优点在于,你可以根据需要选择特定的装饰器。任何被装饰的⽅法都可以同步或异步执⾏,异步执⾏可以采⽤ CompletableFuture 或RxJava。

当有很多超过规定响应时间的请求时,在远程系统没有响应和引发异常之前,断路器将会开启。

当Hystrix处于半开状态时,Hystrix根据只执⾏⼀次请求的结果来决定是否关闭断路器。⽽R4j允许执⾏可配置次数的请求,将请求的结果和配置的阈值进⾏⽐较来决定是否关闭断路器。

R4j提供了⾃定义的Reactor和Rx Java操作符对断路器、隔离机制、限流器中任何的反应式类型进⾏装饰。

Hystrix和R4j都发出⼀个事件流,系统可以对发出的事件进⾏监听,得到相关的执⾏结果和延迟的时间统计数据都是⼗分有⽤的。

4.3 Resilence4j最佳实践

下⾯分三⽅⾯讲解Resilence4j对于微服务容错的处理,分别为熔断,隔离,限流。

4.3.1 断路器(CircuitBreaker)

4.3.1.1.CircuitBreaker简介
    断路器通过有限状态机实现,有三个普通状态:关闭(CLOSED)、开启(OPEN)、半开 (HALF_OPEN),还有两个特殊状态:禁⽤(DISABLED)、强制开启(FORCED_OPEN)。

    当熔断器关闭时,所有的请求都会通过熔断器。如果失败率超过设定的阈值,熔断器就会从关闭状态转换到打开状态,这时所有的请求都会被拒绝。当经过⼀段时间后,熔断器会从打开状态转换到半开状态,这时仅有⼀定数量的请求会被放⼊,并重新计算失败率,如果失败率超过阈值,则变为打开状态,如果失败率低于阈值,则变为关闭状态。

    断路器使⽤滑动窗⼝来存储和统计调⽤的结果。你可以选择基于调⽤数量的滑动窗⼝或者基于时间的滑动窗⼝。基于访问数量的滑动窗⼝统计了最近N次调⽤的返回结果。居于时间的滑动窗⼝统计了最近N秒的调⽤返回结果。

    除此以外,熔断器还会有两种特殊状态:DISABLED(始终允许访问)和FORCED_OPEN(始终拒绝访问)。这两个状态不会⽣成熔断器事件(除状态装换外),并且不会记录事件的成功或者失败。退出这两个状态的唯⼀⽅法是触发状态转换或者重置熔断器。
4.3.1.2.添加依赖
org.springframework.cloud spring-cloud-starter-circuitbreaker-resilience4j
4.3.1.3.CircuitBreaker配置
配置属性 默认值 描述
failureRateThreshold 50 以百分⽐配置失败率阈值。当失败率等于或⼤于阈值时,断路器状态并关闭变为开启,并进⾏服务降级。
slowCallRateThreshold 100 以百分⽐的⽅式配置,断路器把调⽤时间⼤于slowCallDurationThreshold的调⽤视为慢调⽤,当慢调⽤⽐例⼤于等于阈值时,断路器开启,并进⾏服务降级。
slowCallDurationThreshold 60000[ms] 配置调⽤时间的阈值,⾼于该阈值的呼叫视为慢调⽤,并增加慢调⽤⽐例。
permittedNumberOfCallsInHalfOpenState 10 断路器在半开状态下允许通过的调⽤次数。
maxWaitDurationInHalfOpenState 0 断路器在半开状态下的最⻓等待时间,超过该配置值的话,断路器会从半开状态恢复为开启状态。配置是0时表示断路器会⼀直处于半开状态,直到所有允许通过的访问结束。
slidingWindowType COUNT_BASED 配置滑动窗⼝的类型,当断路器关闭时,将调⽤的结果记录在滑动窗⼝中。滑动窗⼝的类型可以是count-based或time-based。如果滑动窗⼝类型是COUNT_BASED,将会统计记录最近slidingWindowSize次调⽤的结果。如果是TIME_BASED,将会统计记录最近 slidingWindowSize秒的调⽤结果。
slidingWindowSize 100 配置滑动窗⼝的⼤⼩。
minimumNumberOfCalls 100 断路器计算失败率或慢调⽤率之前所需的最⼩调⽤数(每个滑动窗⼝周期)。例如,如果minimumNumberOfCalls为10,则必须⾄少记录10个调⽤,然后才能计算13失败率。如果只记录了9次调⽤,即使所有9次调⽤都失败,断路器也不会开启。
waitDurationInOpenState 60000 [ms] 断路器从开启过渡到半开应等待的时间。
automaticTransition FromOpenToHalfOpenEnabled false 如果设置为true,则意味着断路器将⾃动从开启状态过渡到半开状态,并且不需要调⽤来触发转换。创建⼀个线程来监视断路器的所有实例,以便在WaitDurationInOpenstate之后将它们转换为半开状态。但是,如果设置为false,则只有在发出调⽤时才会转换到半开,即使 在waitDurationInOpenState之后也是如此。这⾥的优点是没有线程监视所有断路器的状态。
recordExceptions empty 记录为失败并因此增加失败率的异常列表。除⾮通过ignoreExceptions显式忽略,否则与列表中某个匹配或继承的异常都将被视为失败。 如果指定异常列表,则所有其他异常均视为成功,除⾮它们被ignoreExceptions显式忽略。
ignoreExceptions empty 被忽略且既不算失败也不算成功的异常列表。任何与列表之⼀匹配或继承的异常都不会被视为失败或成功,即使异常是 recordExceptions的⼀部分。
recordException throwable -> true By default all exceptions are recored as failures. ⼀个⾃定义断⾔,⽤于评估异常是否应记录为失败。 如果异常应计为失败,则断⾔必须返回true。如果出断⾔返回false,应算作成功,除⾮ignoreExceptions显式忽略异常。
ignoreException throwable -> false By default no exceptions is ignored. ⾃定义断⾔来判断⼀个异常是否应该被忽略异常,则谓词必须返回 true。如果异常应算作失败,则断⾔必须返回 false

参考表属性,在订单项⽬中配置断路器,代码如下。


resilience4j.circuitbreaker:
    configs:
        default:
            registerHealthIndicator: true
            slidingWindowSize: 10  #滑动窗⼝的⼤⼩,配置COUNT_BASED,表示10个请求,配置TIME_BASED表示10秒
            minimumNumberOfCalls: 5 #最⼩请求个数,只有在滑动窗⼝内,请求个数达到这个个数,才会触发CircuitBreader对于断路器的判断
            permittedNumberOfCallsInHalfOpenState: 3 #当CircuitBreaker处于HALF_OPEN状态的时候,允许通过的请求个数
            automaticTransitionFromOpenToHalfOpenEnabled: true #设置true,表示⾃动从OPEN变成HALF_OPEN,即使没有请求过来
            waitDurationInOpenState: 5s  #从OPEN到HALF_OPEN状态需要等待的时间
            failureRateThreshold: 50  #失败请求百分⽐,超过这个⽐例,CircuitBreaker变为OPEN状态
            slowCallDurationThreshold: 2s #慢调⽤时间阈值,⾼于这个阈值的呼叫视为慢调⽤,并增加慢调⽤⽐例。
            slowCallRateThreshold: 30 #慢调⽤百分⽐阈值,断路器把调⽤时间⼤于    slowCallDurationThreshold,视为慢调⽤,当慢调⽤⽐例⼤于阈值,断路器打开,并进⾏服务降级

            eventConsumerBufferSize: 20  # 表示重试事件缓冲区大小;
            recordExceptions:  #异常名单 记录为失败并因此增加失败率的异常列表。除⾮通过ignoreExceptions显式忽略,否则与列表中某个匹配或继承的异常都将被视为失败。 如果指定异常列表,则所有其他异常均视为成功,除⾮它们被ignoreExceptions显式忽略。
                - org.springframework.web.client.HttpServerErrorException
                - java.util.concurrent.TimeoutException
                - java.io.IOException
                - io.github.robwin.exception.BusinessIoException
            ignoreExceptions:  # 被忽略且既不算失败也不算成功的异常列表。任何与列表之⼀匹配或继承的异常都不会被视为失败或成功,即使异常是 recordExceptions的⼀部分。
                - io.github.robwin.exception.BusinessException
        shared:
            slidingWindowSize: 100
            permittedNumberOfCallsInHalfOpenState: 30
            waitDurationInOpenState: 1s
            failureRateThreshold: 50
            eventConsumerBufferSize: 10
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
    instances:
        backendA:
            baseConfig: default
        backendB:
            registerHealthIndicator: true
            slidingWindowSize: 10
            minimumNumberOfCalls: 10
            permittedNumberOfCallsInHalfOpenState: 3
            waitDurationInOpenState: 5s
            failureRateThreshold: 50
            eventConsumerBufferSize: 10
            recordExceptions:
                - io.github.robwin.exception.BusinessIoException
            recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate
    上⾯配置了2个断路器"backendA",和"backendB",其中backendA断路器配置基于default配置,"backendB"断路器配置了慢调⽤⽐例熔断,"backendA"熔断器配置了异常⽐例熔断

4.OrderController

 @RestController
@RequestMapping(value = "/backendC")
public class BackendCController {
    
    

    private final Service businessCService;

    public BackendCController(@Qualifier("backendCService")Service businessCService){
    
    
        this.businessCService = businessCService;
    }

    @GetMapping("failure")
    public String failure(){
    
    
        return businessCService.failure();
    }
}



     @Component(value = "backendCService")
public class BackendCService implements Service {
    
    

    private static final String BACKEND_C = "backendC";

    @Override
    @CircuitBreaker(name = BACKEND_C)
    @Bulkhead(name = BACKEND_C)
    @Retry(name = BACKEND_C)
    public String failure() {
    
    
        throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "This is a remote exception");
    }

}

猜你喜欢

转载自blog.csdn.net/ywtech/article/details/132627668