Spring Boot集成Resilience4J实现限流/重试/隔离

1.前言

上篇文章讲了Resilience4J实现熔断功能,文章详见:Spring Boot集成Resilience4J实现断路器功能 | Harries Blog™,本篇文章主要讲述基于Resilience4J实现限流/重试/隔离。

2.代码工程

pom.xml

复制代码
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
    <version>2.0.2</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

限流

复制代码
@RequestMapping("/hello")
@RateLimiter(name="ratelimitApi",fallbackMethod = "fallback")
public ResponseEntity<String> showHelloWorld(){
   return new ResponseEntity<>("success",HttpStatus.OK);
   }
public ResponseEntity fallback(Throwable e){
   log.error("fallback exception , {}",e.getMessage());
   return new ResponseEntity<>("your request is too fast,please low down", HttpStatus.OK);
}

重试

复制代码
@RequestMapping("/retry")
@Retry(name = "backendA")//use backendA ,if throw IOException ,it will be retried 3 times。
public ResponseEntity<String> retry(String name){
   if(name.equals("test")){
      i++;
      log.info("retry time:{}",i);
      throw  new HttpServerErrorException(HttpStatusCode.valueOf(101));
   }
   return new ResponseEntity<>("retry",HttpStatus.OK);
}

隔离

复制代码
@RequestMapping("/bulkhead")
@Bulkhead(name = "backendA")
public ResponseEntity<String> bulkhead(){

   return new ResponseEntity<>("bulkhead",HttpStatus.OK);
}

配置文件

复制代码
spring:
  application.name: resilience4j-demo
  jackson.serialization.indent_output: true

management:
  endpoints.web.exposure.include:
    - '*'
  endpoint.health.show-details: always
  health.circuitbreakers.enabled: true

resilience4j:
  circuitbreaker:
    configs:
      default:
        registerHealthIndicator: true
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        permittedNumberOfCallsInHalfOpenState: 3
        automaticTransitionFromOpenToHalfOpenEnabled: true
        waitDurationInOpenState: 5s
        failureRateThreshold: 50
        eventConsumerBufferSize: 10


  ratelimiter: 
    instances:
      ratelimitApi:
        limit-for-period: 5 
        limit-refresh-period: 1s 
        timeout-duration: 100ms 
  retry:
    instances:
      backendA:
        maxAttempts: 3
        waitDuration: 10s
        enableExponentialBackoff: true
        exponentialBackoffMultiplier: 2
        retryExceptions:
          - org.springframework.web.client.HttpServerErrorException
          - java.io.IOException
  bulkhead:
    instances:
      backendA:
        maxConcurrentCalls: 10

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

3.测试

1.启动Spring Boot应用程序

测试限流

复制代码
public class ThreadTest {
    public static void main(String[] args) {
       for(int i=0;i<6;i++){
            new Thread(()->{
                System.out.println(new RestTemplate().getForObject("http://localhost:8080/hello",String.class));
            }).start();
        }
     
    }
}

运行main方法

复制代码
io.github.resilience4j.bulkhead.BulkheadFullException: Bulkhead 'backendA' is full and does not permit further calls
 at io.github.resilience4j.bulkhead.BulkheadFullException.createBulkheadFullException(BulkheadFullException.java:49) ~[resilience4j-bulkhead-2.0.2.jar:2.0.2]
 at io.github.resilience4j.bulkhead.internal.SemaphoreBulkhead.acquirePermission(SemaphoreBulkhead.java:164) ~[resilience4j-bulkhead-2.0.2.jar:2.0.2]
 at io.github.resilience4j.bulkhead.Bulkhead.lambda$decorateCheckedSupplier$0(Bulkhead.java:68) ~[resilience4j-bulkhead-2.0.2.jar:2.0.2]
 at io.github.resilience4j.bulkhead.Bulkhead.executeCheckedSupplier(Bulkhead.java:471) ~[resilience4j-bulkhead-2.0.2.jar:2.0.2]
 at io.github.resilience4j.spring6.bulkhead.configure.BulkheadAspect.handleJoinPoint(BulkheadAspect.java:194) ~[resilience4j-spring6-2.0.2.jar:2.0.2]
 at io.github.resilience4j.spring6.bulkhead.configure.BulkheadAspect.proceed(BulkheadAspect.java:147) ~[resilience4j-spring6-2.0.2.jar:2.0.2]
 at io.github.resilience4j.spring6.bulkhead.configure.BulkheadAspect.lambda$bulkheadAroundAdvice$1(BulkheadAspect.java:120) ~[resilience4j-spring6-2.0.2.jar:2.0.2]
 at io.github.resilience4j.spring6.fallback.FallbackExecutor.execute(FallbackExecutor.java:37) ~[resilience4j-spring6-2.0.2.jar:2.0.2]
 at io.github.resilience4j.spring6.bulkhead.configure.BulkheadAspect.bulkheadAroundAdvice(BulkheadAspect.java:121) ~[resilience4j-spring6-2.0.2.jar:2.0.2]
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
 at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
 at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
 at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:637) ~[spring-aop-6.1.2.jar:6.1.2]

测试重试

访问http://127.0.0.1:8080/retry?name=test

复制代码
2024-08-03T23:16:32.092+08:00 INFO 5097 --- [resilience4j-demo] [nio-8080-exec-9] c.e.r.controller.HelloWorldController : retry time:1
2024-08-03T23:16:42.120+08:00 INFO 5097 --- [resilience4j-demo] [nio-8080-exec-9] c.e.r.controller.HelloWorldController : retry time:2
2024-08-03T23:17:02.142+08:00 INFO 5097 --- [resilience4j-demo] [nio-8080-exec-9] c.e.r.controller.HelloWorldController : retry time:3
2024-08-03T23:17:02.165+08:00 ERROR 5097 --- [resilience4j-demo] [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.web.client.HttpServerErrorException: 101 SWITCHING_PROTOCOLS] with root cause

org.springframework.web.client.HttpServerErrorException: 101 SWITCHING_PROTOCOLS
 at com.et.resilience4j.controller.HelloWorldController.retry(HelloWorldController.java:37) ~[classes/:na]
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
 at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
 at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
 at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:352) ~[spring-aop-6.1.2.jar:6.1.2]
 at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.1.2.jar:6.1.2]

测试隔离

复制代码
public class ThreadTest {
    public static void main(String[] args) {
      /*  for(int i=0;i<6;i++){
            new Thread(()->{
                System.out.println(new RestTemplate().getForObject("http://localhost:8080/hello",String.class));
            }).start();
        }*/
      for(int i=0;i<11;i++){
         new Thread(()->{
            System.out.println(new RestTemplate().getForObject("http://localhost:8080/bulkhead",String.class));
         }).start();
      }
    }
}

运行main方法

复制代码
2024-08-03T23:17:36.943+08:00 ERROR 5097 --- [resilience4j-demo] [nio-8080-exec-5] c.e.r.controller.HelloWorldController : fallback exception , RateLimiter 'ratelimitApi' does not permit further calls
相关推荐
古茗前端团队1 小时前
急招!前端|测试|后端|产品(名额多,速来)
前端·后端·架构
喵个咪2 小时前
Go-Wind HTTP 服务器从入门到精通
后端·http·go
hunterandroid3 小时前
Hilt 依赖注入:从手动 new 到自动装配
后端
喵个咪3 小时前
Go-Wind gRPC 服务器从入门到精通
后端·go·grpc
喵个咪3 小时前
Go-Wind GraphQL 服务器从入门到精通
后端·graphql
青青子衿悠悠我心3 小时前
Docker与Kubernetes的十年战争与融合
后端
AI小老六3 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
云技纵横3 小时前
@Transactional 到底要不要加 rollbackFor?一次数据不一致事故讲清楚
后端·面试
Csvn3 小时前
日志分析进阶 — Logwatch 与 GoAccess 实战
后端