业务场景:
随着服务的模块化,软件架构中系统之间的接口调用也变得越来越多,由于网络抖动或者其他原因造成接口调用失败。没能达到理想效果。
解决方案:
鉴于上述问题,我们第一时间可能想到的就是“重试”,在重试的过程中,我们需要考虑:
1.重试的次数;
2.每次重试间隔的时间;
3.在达到最高重试次数后依旧失败我们的处理方案
4.。。。。。。。。;
Spring提供了retry机制,使得我们不用自己去重复的造轮子,使用简单的注解即可实现以上逻辑。
主要注解:
@EnableRetry:
根据源码,只有一个参数。该参数指明是否使用CGLIB来代理;
@Retryable:
value:指定处理的异常类
include:指定处理的异常类和value一样,默认为空,当exclude也为空时,默认所有异常
exclude:指定异常不处理,默认空,当include也为空时,默认所有异常
maxAttempts:最大重试次数。默认3次
maxAttemptsExpression: 计算最大重试次数的表达式,包含首次失败,默认为3
backoff: 重试等待策略。默认使用@Backoff注解
@Backoff 重试等待策略:
不设置参数时,默认使用FixedBackOffPolicy(指定等待时间),重试等待1000ms
设置delay,使用FixedBackOffPolicy(指定等待时间),重试等待填写的时间
设置delay和maxDealy时,重试等待在这两个值之间均态分布
设置delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指数级重试间隔的实现 ),multiplier即指定延迟倍数,比如delay=5000l,multiplier=2,则第一次重试为5秒,第二次为10秒,第三次为20秒……
@Recover:
用于@Retryable重试失败后处理方法,此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。
代码示例:
1.首先引入需要的jar包:示例基于SpringBoot,且SpringRetry机制依赖于AOP:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<!-- Add typical dependencies for a web application -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
启动类:
@SpringBootApplication @EnableRetry public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); } }
service:
@Service public class DemoService { @Retryable(value = RuntimeException.class,maxAttempts = 4) public void service(){ System.out.println("开始调用服务..."); throw new RuntimeException("服务调用异常"); } @Recover private void callback(RuntimeException e){ System.out.println(e.getMessage()); } }
测试类:
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = Application.class) public class ServiceTest { @Autowired private DemoService service; @Test public void test(){ service.service(); } }
测试结果: