最近組內准備將項目中原有的重試功能抽取出來重構為一個重試平台,由於對重試的功能要求比較高,采用了不少中間件和框架(jimdb,jproxy, Elastic-Job ,JMQ,Hbase, Disruptor ),而且重寫了 BlockingQueue,平台構架也比較復雜,在設計重試平台前,也調研過一些重試的開源框架,Spring Retry映入了眼簾,雖然最后沒有采用它,但是還是想在此處介紹一下它。
在分布式系統中,為了保證數據分布式事務的強一致性,大家在調用RPC接口或者發送MQ時,針對可能會出現網絡抖動請求超時情況采取一下重試操作。大家用的最多的重試方式就是MQ了,但是如果你的項目中沒有引入MQ,那就不方便了,本文主要介紹一下如何使用Spring Retry實現重試操作。
1、引入Spring Retry依賴
<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、在啟動入口加入重試配置
添加@EnableRetry注解
@SpringBootApplication @EnableRetry public class RetryApplication { public static void main(String[] args) throws Exception{ SpringApplication.run(RetryApplication.class, args); } }
3、編寫測試service
@Service public class RetryService { @Retryable(value= {RemoteAccessException.class},maxAttempts = 5,backoff = @Backoff(delay = 5000l,multiplier = 1)) public void retryTest() throws Exception { System.out.println("do something..."); throw new RemoteAccessException("RemoteAccessException...."); } @Recover public void recover(RemoteAccessException e) { System.out.println(e.getMessage()); System.out.println("recover...."); } }
4、測試service
@Configuration @EnableRetry @EnableAspectJAutoProxy(proxyTargetClass=true) public class RetryServiceMain { @Bean public RetryService retryService(){ return new RetryService(); } public static void main(String[] args) throws Exception{ final AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RetryServiceMain.class); final RetryService retryService = applicationContext.getBean(RetryService.class); retryService.retryTest(); } }
Run這個main方法,控制台會打印如下內容
16:12:28.932 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'retryService'
16:12:28.954 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=0
do something... 16:12:28.973 [main] DEBUG org.springframework.retry.backoff.ExponentialBackOffPolicy - Sleeping for 5000 16:12:33.974 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=1 16:12:33.974 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=1 do something... 16:12:33.974 [main] DEBUG org.springframework.retry.backoff.ExponentialBackOffPolicy - Sleeping for 5000 16:12:38.974 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=2 16:12:38.974 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=2 do something... 16:12:38.974 [main] DEBUG org.springframework.retry.backoff.ExponentialBackOffPolicy - Sleeping for 5000 16:12:43.975 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=3 16:12:43.975 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=3 do something... 16:12:43.975 [main] DEBUG org.springframework.retry.backoff.ExponentialBackOffPolicy - Sleeping for 5000 16:12:48.975 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=4 16:12:48.975 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=4 do something... 16:12:48.975 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=5 16:12:48.975 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry failed last attempt: count=5 RemoteAccessException.... recover....
可見方法重試了五次,每次間隔了5秒,第五次失敗后執行了recover方法。
介紹一下幾個注解
- @EnableRetry能否重試。當proxyTargetClass屬性為true時,使用CGLIB代理。默認使用標准JAVA注解。在spring Boot中此參數寫在程序入口即可。
- @Retryable 標注此注解的方法在發生異常時會進行重試
value:指定處理的異常類
include:指定處理的異常類和value一樣,默認為空,當exclude也為空時,默認所有異常
exclude:指定異常不處理,默認空,當include也為空時,默認所有異常
maxAttempts:最大重試次數。默認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拋出的異常,否則無法識別,可以在該方法中進行日志處理。