Spring Retry 是Spring框架中的一個組件,
它提供了自動從新調用失敗操做的能力。這在錯誤多是暫時發生的(如瞬時網絡故障)的狀況下頗有幫助。java
在本文中,咱們將看到使用Spring Retry的各類方式:註解、RetryTemplate以及回調。git
讓咱們首先將spring-retry
依賴項添加到咱們的pom.xml
文件中:github
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.2.5.RELEASE</version> </dependency>
咱們還須要將Spring AOP添加到咱們的項目中:spring
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.8.RELEASE</version> </dependency>
能夠查看Maven Central來獲取最新版本的spring-retry
和spring-aspects
依賴項。sql
要在應用程序中啓用Spring Retry,咱們須要將@EnableRetry
註釋添加到咱們的@Configuration
類:網絡
@Configuration @EnableRetry public class AppConfig { ... }
@Retryable
而不用恢復咱們可使用@Retryable
註解爲方法添加劇試功能:框架
@Service public interface MyService { @Retryable(value = RuntimeException.class) void retryService(String sql); }
在這裏,當拋出RuntimeException時嘗試重試。maven
根據@Retryable的默認行爲,重試最多可能發生3次,重試之間有1秒的延遲。ide
@Retryable
和@Recover
如今讓咱們使用@Recover
註解添加一個恢復方法:測試
@Service public interface MyService { @Retryable(value = SQLException.class) void retryServiceWithRecovery(String sql) throws SQLException; @Recover void recover(SQLException e, String sql); }
這裏,當拋出SQLException
時重試會嘗試運行。 當@Retryable
方法因指定異常而失敗時,@Recover
註解定義了一個單獨的恢復方法。
所以,若是retryServiceWithRecovery
方法在三次嘗試以後仍是拋出了SQLException
,那麼recover()
方法將被調用。
恢復處理程序的第一個參數應該是Throwable
類型(可選)和相同的返回類型。其他的參數按相同順序從失敗方法的參數列表中填充。
@Retryable
的行爲爲了自定義重試的行爲,咱們可使用參數maxAttempts
和backoff
:
@Service public interface MyService { @Retryable( value = SQLException.class, maxAttempts = 2, backoff = @Backoff(delay = 100)) void retryServiceWithCustomization(String sql) throws SQLException; }
這樣最多將有兩次嘗試和100毫秒的延遲。
咱們還能夠在@Retryable
註解中使用properties。
爲了演示這一點,咱們將看到如何將delay
和maxAttempts
的值外部化到一個properties文件中。
首先,讓咱們在名爲retryConfig.properties
的文件中定義屬性:
retry.maxAttempts=2 retry.maxDelay=100
而後咱們指示@Configuration
類加載這個文件:
@PropertySource("classpath:retryConfig.properties") public class AppConfig { ... } // ...
最後,咱們能夠在@Retryable
的定義中注入retry.maxAttempts
和retry.maxDelay
的值:
@Service public interface MyService { @Retryable( value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}", backoff = @Backoff(delayExpression = "${retry.maxDelay}")) void retryServiceWithExternalizedConfiguration(String sql) throws SQLException; }
請注意,咱們如今使用的是maxAttemptsExpression
和delayExpression
而不是maxAttempts
和delay
。
RetryTemplate
RetryOperations
Spring Retry提供了RetryOperations
接口,它提供了一組execute()
方法:
public interface RetryOperations { <T> T execute(RetryCallback<T> retryCallback) throws Exception; ... }
execute()
方法的參數RetryCallback
,是一個接口,能夠插入須要在失敗時重試的業務邏輯:
public interface RetryCallback<T> { T doWithRetry(RetryContext context) throws Throwable; }
RetryTemplate
配置RetryTemplate
是RetryOperations
的一個實現。
讓咱們在@Configuration
類中配置一個RetryTemplate
的bean:
@Configuration public class AppConfig { //... @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(2000l); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(2); retryTemplate.setRetryPolicy(retryPolicy); return retryTemplate; } }
這個RetryPolicy
肯定了什麼時候應該重試操做。
其中SimpleRetryPolicy
定義了重試的固定次數,另外一方面,BackOffPolicy
用於控制重試嘗試之間的回退。
最後,FixedBackOffPolicy
會使重試在繼續以前暫停一段固定的時間。
RetryTemplate
要使用重試處理來運行代碼,咱們能夠調用retryTemplate.execute()
方法:
retryTemplate.execute(new RetryCallback<Void, RuntimeException>() { @Override public Void doWithRetry(RetryContext arg0) { myService.templateRetryService(); ... } });
咱們可使用lambda表達式代替匿名類:
retryTemplate.execute(arg0 -> { myService.templateRetryService(); return null; });
監聽器在重試時提供另外的回調。咱們能夠用這些來關注跨不一樣重試的各個橫切點。
回調在RetryListener
接口中提供:
public class DefaultListenerSupport extends RetryListenerSupport { @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { logger.info("onClose"); ... super.close(context, callback, throwable); } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { logger.info("onError"); ... super.onError(context, callback, throwable); } @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { logger.info("onOpen"); ... return super.open(context, callback); } }
open
和close
的回調在整個重試以前和以後執行,而onError
應用於單個RetryCallback
調用。
接下來,咱們將咱們的監聽器(DefaultListenerSupport)
註冊到咱們的RetryTemplate
bean:
@Configuration public class AppConfig { ... @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); ... retryTemplate.registerListener(new DefaultListenerSupport()); return retryTemplate; } }
爲了完成咱們的示例,讓咱們驗證一下結果:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( classes = AppConfig.class, loader = AnnotationConfigContextLoader.class) public class SpringRetryIntegrationTest { @Autowired private MyService myService; @Autowired private RetryTemplate retryTemplate; @Test(expected = RuntimeException.class) public void givenTemplateRetryService_whenCallWithException_thenRetry() { retryTemplate.execute(arg0 -> { myService.templateRetryService(); return null; }); } }
從測試日誌中能夠看出,咱們已經正確配置了RetryTemplate
和RetryListener
:
2020-01-09 20:04:10 [main] INFO c.p.s.DefaultListenerSupport - onOpen 2020-01-09 20:04:10 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService() 2020-01-09 20:04:10 [main] INFO c.p.s.DefaultListenerSupport - onError 2020-01-09 20:04:12 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService() 2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onError 2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onClose
在本文中,咱們看到了如何使用註解、RetryTemplate
和回調監聽器來使用Spring Retry。
原文地址:https://www.baeldung.com/spring-retry
翻譯:碼農熊貓
更多技術乾貨,請訪問個人我的網站https://pinmost.com,或關注公衆號【碼農熊貓】