10.3 Resilience4j 结合微服务
Retry、CircuitBreaker、RateLimiter
10.3.1 Retry
- (1)创建一个 Spring Boot 项目 resilience4j-2 作为 module,添加 web 、eureka client discovery 依赖
- (2)手动添加 Resilience4j 依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.5.0</version>
</dependency>
- (3)把原来的 application.properties 文件删除,新建 application.yml 文件来配置 retry 重试,配置注册到 Eureka
resilience4j:
retry:
retry-aspect-order: 399 #表示 retry 的优先级,默认高于限流、断路器,数值越小,优先级越高
backends:
retryA: # 自定义策略 retryA
maxRetryAttempts: 5 #重试次数
waitDuration: 500 #重试等待时间
exponentialBackoffMultiplier: 1.1 #间隔乘数,第一次 1.1秒,第二次 1.21 秒
retryExceptions: # 触发重试的异常
- java.lang.RuntimeException
# 项目名及 eureka 配置
spring:
application:
name: resilience4j
server:
port: 5000
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka
- (4)在 provider 服务中,设置本身会抛异常的接口:
public String hello(){
String s = "hello " + port;
System.out.println(s);
int i = 1/0;
return s;
}
- (5)resilience4j-2 服务中,创建 RestTemplate 请求模板 bean对象,创建 HelloService 和 HelloController 类
@SpringBootApplication
public class Resilience4j2Application {
public static void main(String[] args) {
SpringApplication.run(Resilience4j2Application.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
@Service
@Retry(name="retryA")//表示使用的重试策略
public class HelloService {
@Autowired
RestTemplate restTemplate;
public String hello(){
String s = restTemplate.getForObject("http://provider/hello", String.class);
return s;
}
}
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}
在 HelloService 中的 @Retry(name = "retryA")
注解的属性 retryA
是在 application.yml
中配置自定义的策略名;
- (6)启动 Eureka 服务端、provider 服务和 resilience4j-2 服务,访问 http://localhost:5000/hello 地址:
前端报错
-(7)查看 provider 控制台日志,看接口中被访问情况:
hello 1113 被打印了5次,说明接口被访问5次,就是说 resilience4j-2 的 retry 重试功能起作用了,重试了 5次。
10.3.2 CircuitBreaker 断路器
- (1)继续在 application.yml中配置断路器
resilience4j:
retry:
retry-aspect-order: 399 #表示 retry 的优先级,默认高于限流、断路器,数值越小,优先级越高
backends:
retryA: # 自定义策略 retryA
maxRetryAttempts: 5 #重试次数
waitDuration: 500 #重试等待时间
exponentialBackoffMultiplier: 1.1 #间隔乘数,第一次 1.1秒,第二次 1.21 秒
retryExceptions: # 触发重试的异常
- java.lang.RuntimeException
# 断路器配置
circuitbreaker:
instances:
cbA: # 实例自定义名字
ringBufferSizeInClosedState: 5
ringBufferSizeInHalfOpenState: 3
waitInterval: 5000 #断路器从 open 切换到 half open 需要保持的时间间隔
recordException: # 服务出现异常,就熔断,降级
- org.springframework.web.client.HttpServerErrorException
circuit-breaker-aspect-order: 398
# 项目名及 eureka 配置
spring:
application:
name: resilience4j
server:
port: 5000
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka
- (2)在 HelloServie 类上加上
@CircuitBreaker
注解,并配置fallbackMethod
属性
@Service
@Retry(name="retryA")
@CircuitBreaker(name = "cbA",fallbackMethod = "error")
public class HelloService {
@Autowired
RestTemplate restTemplate;
public String hello(){
return restTemplate.getForObject("http://provider/hello", String.class);
}
public String error(Throwable t){
return "error";
}
}
注意 error 方法的参数要加上Throwable
参数,否则会报错,继续访问 hello 接口,返回 error ,说明服务降级了
- 在 provider 中,我们依然能看见 控制台打印 5次 hello 1113,证明 retry 也在起作用
- (3)retry 和 circuitbreker 一起使用
在 provider 中更改接口,使请求重试在第二次成功,
@RestController
public class HelloController implements IUserService{
@Value("${server.port}")
Integer port;
@Override
public String hello(){
String s = "hello " + port;
System.out.println(s);
if(port++ != 1114){
int i = 1/0;
}
return s;
}
}
然后重启provider ,继续访问 hello 接口:
前端结果成功
provider 服务控制台日志
打印了两次,说明 retry 的第二次成功了,服务不进行降级。成功返回了结果 hello 1114
10.3.3 RateLimiter
可以在请求端,也可以在被请求端使用,主要在被请求端使用,保护服务端的接口
- (1)在 provider 中添加 Resilience4j 依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.5.0</version>
</dependency>
- (2)在 application.properties 中配置限流
# 一个周期有几次请求
resilience4j.ratelimiter.instances.rlA.limit-for-period=2
# 一个周期的刷新时间
resilience4j.ratelimiter.instances.rlA.limit-refresh-period=1s
resilience4j.ratelimiter.instances.rlA.timeout-duration=1s
就是一秒处理两个请求
- (3)修改 provider 中的 hello 接口,打印时间,方便观察限流效果
@RateLimiter(name = "rlA")
public String hello(){
String s = "hello " + port;
System.out.println(new Date());
return s;
}
@RateLimiter(name = "rlA")
注解的 rlA
是 application.properties
中配置的自定义限流策略名称。
- (4)在 resilience4j-2 中修改 HelloService 方法, 多次调用 provider 中的接口
@Service
@Retry(name="retryA")
@CircuitBreaker(name = "cbA",fallbackMethod = "error")
public class HelloService {
@Autowired
RestTemplate restTemplate;
public String hello(){
for (int i = 0;i < 5;i++){
String s = restTemplate.getForObject("http://provider/hello", String.class);
}
return "success";
}
public String error(Throwable t){
return "error";
}
}
- (5)重启 provider 和 resilience4j-2 服务,再次调用 http:///localhost:5000/hello,观察 provider 控制台打印情况:
如果修改application.properties
每秒处理一个请求
# 一秒处理一个请求
resilience4j.ratelimiter.instances.rlA.limit-for-period=1
resilience4j.ratelimiter.instances.rlA.limit-refresh-period=1s
resilience4j.ratelimiter.instances.rlA.timeout-duration=1s
再次查看 provider 的控制台
一秒处理一个请求也起了作用,这就是限流的简单应用