Spring Cloud 学习笔记 —— Resilience4 微服务中基本用法

    科技2022-09-06  124

    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 的控制台 一秒处理一个请求也起了作用,这就是限流的简单应用

    Processed: 0.008, SQL: 9