使用Resilience4j保护实现容错-限流

  • Resilience4j快速使用
  • 断路器 Circuit breaking
  • 限流
  • 仓壁模式
  • 重试
  • 注解配合使用与执行顺序
  • Resilience4j配置管理
  • 配置可视化
  • 默认配置
  • 配置共享
  • 动态配置
  • Feign与Resilience4j


Resilience4j快速使用

在微服务中,经常会出现一些故障,而一些故障会直接或者间接的拖垮其它的服务,造成服务器雪崩,系统就会死掉。

假如现在有很多的用户同时请求订单微服务去执行下单的操作,那么会调用我们的支付微服务,如果支付微服务现在挂掉了,而订单调用一直没有响应,由于很多的用户执行相同的操作,属于高并发,那么服务器上积累的订单越来越多,那么原来没有问题的订单微服务,也会被拖垮,这就是服务雪崩。

我们需要做的就是,当某一个微服务发生蔓延当时候,不能发生故障蔓延,整个系统还能以其它某种方式正常运行,这个就是我们需要解决的。

Resilience4j是一个轻量级的容错框架,灵感来自于Netflix的Hystrix
提供了如下几款核心组件:

  • resilience4j-circuitbreaker: Circuit breaking
  • resilience4j-ratelimiter: Rate limiting
  • resilience4j-bulkhead: Bulkheading
  • resilience4j-retry: Automatic retrying (sync and async)
  • resilience4j-cache: Response caching

官方文档

pom依赖

<dependency>
     <groupId>io.github.resilience4j</groupId>
      <artifactId>resilience4j-spring-cloud2</artifactId>
      <version>1.1.0</version>
  </dependency>

断路器 Circuit breaking

@CircuitBreaker(name = "detail")
@GetMapping("detail")
public HttpResponseBody detail(@RequestParam("id") Long id){
    return HttpResponseBody.successResponse("ok",productServiceClient.detail(id));
}

断路器相关配置

Resilience4j支持集群限流吗 resilience4j feign_Resilience4j支持集群限流吗

限流

  1. 使用 @RateLimiter注解,指定限流的方法
@RateLimiter(name = "detail")
 @GetMapping("detail")
 public HttpResponseBody detail(@RequestParam("id") Long id){
     return HttpResponseBody.successResponse("ok",productServiceClient.detail(id));
 }
  1. 添加限流配置,一下配置代表一秒内只允许一次请求
resilience4j:
  ratelimiter:
    instances:
      # 与注解内的name相同
      detail:
        #在刷新周期内,请求最大频次
        limitForPeriod: 1
        #刷新周期
        limitRefreshPeriod: 1s
        #线程等待许可的时间
        timeoutDuration: 0

Resilience4j支持集群限流吗 resilience4j feign_限流_02


如果不想返回错误可以这样写

@RateLimiter(name = "detail",fallbackMethod = "detailFallBack")
 @GetMapping("detail")
 public HttpResponseBody detail(@RequestParam("id") Long id){
     return HttpResponseBody.successResponse("ok",productServiceClient.detail(id));
 }

 public HttpResponseBody detailFallBack(Long id,Throwable throwable){
     return HttpResponseBody.failResponse("服务已经限流");
 }

Resilience4j支持集群限流吗 resilience4j feign_Resilience4j支持集群限流吗_03

限流相关配置

Resilience4j支持集群限流吗 resilience4j feign_限流_04

仓壁模式

@GetMapping("detail")
 @Bulkhead(name = "detail",fallbackMethod = "detailFallBack")
 public HttpResponseBody detail(@RequestParam("id") Long id) throws InterruptedException {
     Thread.sleep(2000L);
     return HttpResponseBody.successResponse("ok",productServiceClient.detail(id));
 }

配置文件

resilience4j:
  bulkhead:
    instances:
      detail:
        #最大并发请求,默认25
        maxConcurrentCalls: 1

仓壁模式有两种实现方式

  1. 基于Semaphore(默认)
  2. 基于ThreadPool
@GetMapping("detail")
@Bulkhead(name = "detail",type = Bulkhead.Type.THREADPOOL,fallbackMethod = "detailFallBack")
public HttpResponseBody detail(@RequestParam("id") Long id) throws InterruptedException {
    Thread.sleep(2000L);
    return HttpResponseBody.successResponse("ok",productServiceClient.detail(id));
}

Resilience4j支持集群限流吗 resilience4j feign_限流_05

重试

@GetMapping("detail")
@Retry(name = "detail")
public HttpResponseBody detail(@RequestParam("id") Long id) throws InterruptedException {
     Thread.sleep(2000L);
     return HttpResponseBody.successResponse("ok",productServiceClient.detail(id));
 }

Resilience4j支持集群限流吗 resilience4j feign_微服务_06

注解配合使用与执行顺序

如果一个方法上使用了多个注解,那么他们的执行顺序是怎样的呢?

  1. 打开注解
  2. 找到具体的处理类
  3. 类最后有个方法getOrder
@Override
public int getOrder() {
	return properties.getRateLimiterAspectOrder();
}

Resilience4j支持集群限流吗 resilience4j feign_限流_07


值越小优先级越高

  • BulkHead(LOWEST_PRECEDENCE)
  • RateLimiter(LOWEST_PRECEDENCE - 1)
  • CircuitBreaker(LOWEST_PRECEDENCE-2)
  • Retry(LOWEST_PRECEDENCE - 3)

Resilience4j配置管理

配置可视化

以限流为例,通过这段代码可以查看所有限流的配置,方便排查问题

@Autowired
private RateLimiterRegistry rateLimiterRegistry;

 @RequestMapping("config")
 public Seq<RateLimiter> test(){
     return rateLimiterRegistry.getAllRateLimiters();
 }

Resilience4j支持集群限流吗 resilience4j feign_Resilience4j支持集群限流吗_08

默认配置

对于RateLimiter,仅当指定名称的RateLimiter没有任何自定义配置时,名为default的配置才有效

resilience4j:
  ratelimiter:
    configs:
      default:
        #在刷新周期内,请求最大频次
        limitForPeriod: 10
        #刷新周期
        limitRefreshPeriod: 1s
        #线程等待许可的时间
        timeoutDuration: 0

默认配置是懒加载的, 只有请求限流的接口,才会生效

第一次请求

Resilience4j支持集群限流吗 resilience4j feign_微服务_09

请求了限流接口后的配置

Resilience4j支持集群限流吗 resilience4j feign_刷新周期_10

配置共享

Resilience4j的配置可以说是非常灵活的,当有多个实例配置限流时,可通过baseConfig引用相同配置

resilience4j:
  ratelimiter:
    instances:
      detail:
        #刷新周期
        limitRefreshPeriod: 1s
        baseConfig: share
    configs:
      share:
        limitForPeriod: 2
      default:
        #在刷新周期内,请求最大频次
        limitForPeriod: 10
        #刷新周期
        limitRefreshPeriod: 1s

动态配置

可以通过配置中心实现动态配置,如放到consule、nacos等等这里就不在演示

Feign与Resilience4j

使用方式跟上面说的没多大区别,唯独fallbackMethod略有不同,这里要使用default是标识方法

Logger log = LoggerFactory.getLogger(ProductServiceClient.class);
    
 @RateLimiter(name = "detail",fallbackMethod = "detail")
 @RequestMapping(value = "/detailById", method = RequestMethod.GET)
 KillGoodsSpecPriceDetailVo detail(@RequestParam("id") Long id);


 default KillGoodsSpecPriceDetailVo detail( Long id,Throwable throwable){
     log.error("发生fallback",throwable);
     return new KillGoodsSpecPriceDetailVo();
 }