大家好,我是热爱撸码的开源大叔。
最近让小妹做的一个需求,需要调用第三方接口。正常情况下,接口的响应是符合要求的,只有在网络抖动等极少数的情况下,会存在超时情况。因为是小概率事件,所以一次超时之后,进行一次重试操作应该就可以了。小妹提交了代码,让我做 code review。代码差不多是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Controller public class RetryController { @Autowired private RetryRequestService retryRequestService;
public String doSth(String param) { int count = 0; String result = ""; while (count < 3) { try { result = retryRequestService.request(param); break; } catch (Exception e) { count++; } } return "响应是" + result; } }
|
大叔看完之后,感觉通过固定次数的循环来实现重试,虽然功能实现了,但是不够简洁呀,对原有代码逻辑的侵入性太强了。于是我给小妹推荐了超好用的重试框架,spring-retry。
简介与快速接入
spring-retry 是 Spring 全家桶中提供的开源重试框架,如果你用的是 Spring Boot 项目,那么接入起来会非常简单,只需要三步即可实现快速接入。
第一步,引入依赖
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency>
|
第二步,在启动类上加注解 @EnableRetry
此举是让你的 Spring Boot 项目支持 spring-retry 的重试功能。像这样:
1 2 3 4 5 6 7 8
| @SpringBootApplication @EnableRetry public class ApplicationServer { public static void main(String[] args) { SpringApplication.run(ApplicationServer.class, args); System.out.println("startApp success!"); } }
|
第三步,在需要重试的方法上加注解 @Retryable
如开头所说,我们需要重试的方法是RetryRequestService#request
这个方法。那么我们就在这个方法上加@Retryable
注解。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service @Slf4j public class RetryRequestService { @Autowired private OtherSystemSpi otherSystemSpi;
@Retryable(value = RuntimeException.class, maxAttempts = 5, backoff = @Backoff(delay = 100)) public String request(String param) { double random = Math.random(); log.info("请求进来了,随机值为:" + random); if (random > 0.1) { throw new RuntimeException("超时"); } return otherSystemSpi.request(param); } }
|
当然,我们这里写了个调皮的逻辑来模拟超时。如果随机值大于0.1则抛出一个RuntimeException
异常。每次请求进来时都会输出日志。
我来解释一下@Retryable
注解中的信息。
- value = RuntimeException.class:是指方法抛出
RuntimeException
异常时,进行重试。这里可以指定你想要拦截的异常。
- maxAttempts:是最大重试次数。如果不写,则是默认 3 次。
- backoff = @Backoff(delay = 100):是指重试间隔。delay=100 意味着下一次的重试,要等 100 毫秒之后才能执行。
我们来执行一下,可以看到日志输出:
1 2
| 2022-03-15 23:51:19.762 INFO 3343 --- [main] c.o.service.retry.RetryRequestService : 请求进来了,随机值为:0.11030214774098712 2022-03-15 23:51:19.867 INFO 3343 --- [main] c.o.service.retry.RetryRequestService : 请求进来了,随机值为:0.09624689154608002
|
前两次的随机值都大于 0.1,所以进行了重试,而且注意时间,都是间隔了大概 100 毫秒输出的日志。第三次的随机值小于 0.1,就直接返回了。
我又试了几次,使五次请求的随机值都大于 0.1 ,则结果是进行了五次请求,最后抛出了个异常。
1 2 3 4 5 6 7 8
| 2022-03-15 23:52:58.201 INFO 3449 --- [main] c.o.service.retry.RetryRequestService : 请求进来了,随机值为:0.5265644192525288 2022-03-15 23:52:58.303 INFO 3449 --- [main] c.o.service.retry.RetryRequestService : 请求进来了,随机值为:0.6343538744876432 2022-03-15 23:52:58.407 INFO 3449 --- [main] c.o.service.retry.RetryRequestService : 请求进来了,随机值为:0.5482463853575078 2022-03-15 23:52:58.511 INFO 3449 --- [main] c.o.service.retry.RetryRequestService : 请求进来了,随机值为:0.5624923285641071 2022-03-15 23:52:58.616 INFO 3449 --- [main] c.o.service.retry.RetryRequestService : 请求进来了,随机值为:0.305945622979098 Exception in thread "main" java.lang.RuntimeException: 超时 at com.opensource.service.retry.RetryRequestService.request(RetryRequestService.java:24) at com.opensource.service.retry.RetryRequestService$$FastClassBySpringCGLIB$$50f0bdca.invoke(<generated>)
|
总结
怎么样,spring-retry 的接入和使用是不是非常简单?只需要一个注解,就可以省去你自己写的复杂的重试代码,方便省心。而且它还支持重试间隔时间随机等更多的功能,需要大家自己探索啦。今天就分享到这里,在公众号后台回复「小分队」即可获取开源项目的地址啦~