一、前言

Resilience4j是一款轻量级,易于使用的容错库,其灵感来自于Netflix Hystrix,但是专为Java 8和函数式编程而设计。轻量级,因为库只使用了Vavr,它没有任何其他外部依赖下。相比之下,Netflix Hystrix对Archaius具有编译依赖性,Archaius具有更多的外部库依赖性。

Resilience4j是一个轻量级、易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计。

Resilience4j提供高阶函数(decorators)来增强任何功能接口、lambda表达式或方法引用,包括断路器、速率限制器、重试或舱壁。可以在任何函数接口、lambda表达式或方法引用上使用多个装饰器。

circuitbreaker组件实现了断路器功能,是基于内存的断路器,采用ConcurrentHashMap来实现。

功能特性:

  • 断路器(Circuit Breaker):在服务出现故障时自动熔断,防止请求继续失败导致雪崩效应。
  • 限流(Rate Limiter):限制请求的并发数或速率,防止系统被过载。
  • 重试(Retry):在请求失败时自动重试一定次数,增加系统的可靠性。
  • 超时(Timeout):设置请求的最大执行时间,防止请求长时间阻塞。
  • Bulkhead:通过限制同时执行的请求数量,保护系统的部分资源不被耗尽。
二、代码工程

1.引入依赖

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>1.7.0</version>
        </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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

2.相关配置文件

# Resilience4J相关配置
resilience4j.circuitbreaker.configs.default.register-health-indicator=true
resilience4j.circuitbreaker.configs.default.sliding-window-size=10
resilience4j.circuitbreaker.configs.default.minimum-number-of-calls=5
resilience4j.circuitbreaker.configs.default.permitted-number-of-calls-in-half-open-state=3
resilience4j.circuitbreaker.configs.default.automatic-transition-from-open-to-half-open-enabled=true
resilience4j.circuitbreaker.configs.default.wait-duration-in-open-state=5s
resilience4j.circuitbreaker.configs.default.failure-rate-threshold=50
resilience4j.circuitbreaker.configs.default.event-consumer-buffer-size=10
# Resilience4J限流相关配置
#限制连续请求5次
resilience4j.ratelimiter.instances.rateLimitTest.limit-for-period=5
#1s刷新统计值
resilience4j.ratelimiter.instances.rateLimitTest.limit-refresh-period=1s
#超时等待时长
resilience4j.ratelimiter.instances.rateLimitTest.timeout-duration=100ms

#重试相关配置
#重试次数
resilience4j.retry.instances.backendTest.max-attempts=3
#重试等待时间
resilience4j.retry.instances.backendTest.wait-duration=10s
resilience4j.retry.instances.backendTest.enable-exponential-backoff=true
resilience4j.retry.instances.backendTest.exponential-backoff-multiplier=2
resilience4j.retry.instances.backendTest.retry-exceptions[0]=org.springframework.web.client.HttpServerErrorException
resilience4j.retry.instances.backendTest.retry-exceptions[1]=java.io.IOException
#隔离相关配置
resilience4j.bulkhead.instances.backendTest.max-concurrent-calls=10
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

3.限流控制层

package com.example.dataproject.controller;

import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qx
 * @date 2024/8/5
 * @des
 */
@RestController
@Slf4j
public class RateLimitController {

    /**
     * 限流测试
     */
    @RequestMapping("/rateLimit")
    @RateLimiter(name = "rateLimitTest", fallbackMethod = "rateLimitFallback")
    public ResponseEntity<String> rateLimitTest() {
        return new ResponseEntity<>("success", HttpStatus.OK);
    }

    /**
     * 限流异常回调方法
     */
    public ResponseEntity rateLimitFallback(Throwable e) {
        log.error("fallback exception,{}", e.getMessage());
        return new ResponseEntity("您的请求速度过快,请求失败!", HttpStatus.OK);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

4.重试控制层

package com.example.dataproject.controller;

import io.github.resilience4j.retry.annotation.Retry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpServerErrorException;

/**
 * @author qx
 * @date 2024/8/5
 * @des 重试控制层
 */
@RestController
@Slf4j
public class RetryController {

    private static int i = 0;

    @RequestMapping("/retry")
    @Retry(name = "backendTest")
    public ResponseEntity<String> retryTest(String name) {
        if ("test".equals(name)) {
            i++;
            log.info("retry time:{}", i);
            throw new HttpServerErrorException(HttpStatus.BAD_REQUEST);
        }
        return new ResponseEntity<>("retry", HttpStatus.OK);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

5.隔离控制层

package com.example.dataproject.controller;

import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qx
 * @date 2024/8/5
 * @des 隔离控制层
 */
@RestController
@Slf4j
public class BulkheadController {

    @RequestMapping("/bulkhead")
    @Bulkhead(name = "backendTest")
    public ResponseEntity<String> bulkhead() {
        return new ResponseEntity<>("bulkhead", HttpStatus.OK);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
三、测试

启动程序进行项目测试。

1.测试限流

package com.example.dataproject.test;

import org.springframework.web.client.RestTemplate;

/**
 * @author qx
 * @date 2024/8/5
 * @des
 */
public class RateLimitTest {
    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(new RestTemplate().getForObject("http://localhost:8080/rateLimit", String.class));
            }).start();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

执行程序,我们发现前5个请求都执行成功了,但是后面的一个请求由于限流的限制,提示请求失败!

SpringBoot集成Resilience4J实现限流/重试/隔离学习_Resilience4j

2.测试重试

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

发现请求重试了3次

SpringBoot集成Resilience4J实现限流/重试/隔离学习_重试_02

3.测试隔离 

package com.example.dataproject.test;

import org.springframework.web.client.RestTemplate;

/**
 * @author qx
 * @date 2024/8/5
 * @des 测试隔离
 */
public class BulkheadTest {
    public static void main(String[] args) {
        for (int i = 0; i < 11; i++) {
            new Thread(() -> {
                System.out.println(new RestTemplate().getForObject("http://localhost:8080/bulkhead", String.class));
            }).start();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

SpringBoot集成Resilience4J实现限流/重试/隔离学习_重试_03