一、前言
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>
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
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);
}
}
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);
}
}
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.测试限流
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();
}
}
}
执行程序,我们发现前5个请求都执行成功了,但是后面的一个请求由于限流的限制,提示请求失败!
2.测试重试
访问http://localhost:8080/retry?name=test
发现请求重试了3次
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();
}
}
}