本文概览:介绍了两种重试机制:自定义和spring的@Retryable。
1 重试的引入
在调用外部服务进行查询时,常常因为网络抖动、服务方进行限流等因素造成查询失败。为了克服这些问题,引入了重试机制。
2 自定义重试的实现
如下在查询时,可以通过重试策略,在服务方导致查询失败时进行重试,而不是立刻返回失败异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
/** * 尝试5次 */ private static final int TRY_COUNT = 5; /** * 等待1s */ private static final long WAIT_MILLS = 1000; private List<Recvable> queryRecvablesByPayRecvableIds(List<String> payRecvableIdList) { // 1、构造查询条件 if (CollectionUtils.isEmpty(payRecvableIdList)) { return Lists.newArrayList(); } RecvableExample recvableExample = new RecvableExample(); recvableExample.createCriteria().andRecvableIdIn(payRecvableIdList); // 2、重试策略实现,TRY_COUNT是重试次数;WAIT_MILLS是等待毫秒数 List<Recvable> queryResult = Lists.newArrayList(); for (int i = 1; i <= TRY_COUNT; i++) { try { queryResult = recvableMapper.selectByExample(recvableExample); } catch (Exception e) { logger.info("try query---"); if (i == TRY_COUNT) { logger.error("查询收款单重试失败"); throw new RuntimeException("查询收款单,重试失败"); } try { Thread.sleep(WAIT_MILLS); } catch (Exception ex) { logger.error("sleep error"); } continue; } break; } return queryResult; } |
3 基于@Retryable的实现
1、pom.xml
spring版本选择3.2.3.RELEASE。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!--配置重试beg--> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.1.4.RELEASE</version> </dependency> <!--配置重试end--> <!--配置 Spring-AOP start--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${org.springframework.version}</version> </dependency> <!--配置 Spring-AOP end --> |
2、spring配置文件
1 2 3 4 5 |
# aop配置 <aop:aspectj-autoproxy proxy-target-class="true"/> # 重试需要定义此Bean <bean id="retryConfiguration" class="org.springframework.retry.annotation.RetryConfiguration"></bean> |
3、代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Service public class ExecuteWithRetry { private static final Logger logger = LoggerFactory.getLogger(ExecuteWithRetry.class); /** * 重试次数 */ private static final int TRY_COUNT = 3; /** * 暂停1s */ private static final long SLEEP_TIME = 1000; @Retryable(maxAttempts = TRY_COUNT, backoff = @Backoff(delay = SLEEP_TIME), value = {RuntimeException.class}) public void query() { logger.info("execute query,time={}", System.currentTimeMillis()); if (true) { throw new RuntimeException("error"); } } } |
关于上面@Retryable的说明:
- maxAttempts,尝试次数
- backoff,可以指定暂停时间
- value和inclue一样,当出现这些异常时,进行重试。
4、测试
1 2 3 4 5 6 7 8 9 10 |
@ContextConfiguration("classpath:applicationContext.xml") @RunWith(SpringJUnit4ClassRunner.class) public class ExecuteWithRetryTest { @Resource private ExecuteWithRetry excuteWithRetry; @Test public void testExcute(){ excuteWithRetry.query(); } } |
执行结果为
23:42:57.450 [main] INFO service.ExecuteWithRetry – execute query,time=1506354177450
23:42:58.455 [main] INFO service.ExecuteWithRetry – execute query,time=1506354178455
23:42:59.459 [main] INFO service.ExecuteWithRetry – execute query,time=1506354179459
java.lang.RuntimeException: error
at service.ExecuteWithRetry.query(ExecuteWithRetry.java:32)
4 相关属性
1、关于backoff中的maxDelay属性
如下一个配置
1 2 3 |
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 20000,multiplier = 2), value = {RuntimeException.class}) |
在上面配置中,我们期望第一次间隔40s执行,第二次40s,第三次80s,等待时间程指数增长。但是发现第一次间隔时间是20s,然后剩下两次都是30秒,原因是这里有个maxdelay属性,默认是30s。时间间隔是取{delay,maxDelay}的最小值。所以上面maxDelay设置为80s(或者大于80s)就可以了,应该设置为:
1 |
backoff = @Backoff(delay = 20000,maxDelay=80000,multiplier = 2) |
5 重试原则
1、查询可以进行重试
2、写操作要慎重,除非业务方支持重入
6 相关问题
1 内嵌函数不生效
1 2 3 4 5 6 7 8 9 10 11 |
public class Test{ public void a(){ b(); } @Retryalbe public void b(){ } } |
7 SpringBoot使用retryable
1、maven配置
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> |
2、在application添加@EnableRetry
1 2 3 4 |
@SpringBootApplication @EnableRetry public class AdminApplication { } |
3、在相应函数上添加@Retryable。
(全文完)