1、概述
Spring Retry 是Spring框架中的一個元件,
它提供了自動重新呼叫失敗操作的能力。這在錯誤可能是暫時發生的(如瞬時網路故障)的情況下很有幫助。
在本文中,我們將看到使用Spring Retry的各種方式:註解、RetryTemplate以及回撥。
2、Maven依賴
讓我們首先將spring-retry
依賴項新增到我們的pom.xml
檔案中:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
我們還需要將Spring AOP新增到我們的專案中:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
可以檢視Maven Central來獲取最新版本的spring-retry
和spring-aspects
依賴項。
3、開啟Spring Retry
要在應用程式中啟用Spring Retry,我們需要將@EnableRetry
註釋新增到我們的@Configuration
類:
@Configuration
@EnableRetry
public class AppConfig { ... }
4、使用Spring Retry
4.1、@Retryable
而不用恢復
我們可以使用@Retryable
註解為方法新增重試功能:
@Service
public interface MyService {
@Retryable(value = RuntimeException.class)
void retryService(String sql);
}
在這裡,當丟擲RuntimeException時嘗試重試。
根據@Retryable的預設行為,重試最多可能發生3次,重試之間有1秒的延遲。
4.2、@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
型別(可選)和相同的返回型別。其餘的引數按相同順序從失敗方法的引數列表中填充。
4.3、自定義@Retryable
的行為
為了自定義重試的行為,我們可以使用引數maxAttempts
和backoff
:
@Service
public interface MyService {
@Retryable( value = SQLException.class,
maxAttempts = 2, backoff = @Backoff(delay = 100))
void retryServiceWithCustomization(String sql) throws SQLException;
}
這樣最多將有兩次嘗試和100毫秒的延遲。
4.4、使用Spring Properties
我們還可以在@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
。
5、RetryTemplate
5.1、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;
}
5.2、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
會使重試在繼續之前暫停一段固定的時間。
5.3、使用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;
});
6、監聽器
監聽器在重試時提供另外的回撥。我們可以用這些來關注跨不同重試的各個橫切點。
6.1、新增回撥
回撥在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
呼叫。
6.2、註冊監聽器
接下來,我們將我們的監聽器(DefaultListenerSupport)
註冊到我們的RetryTemplate
bean:
@Configuration
public class AppConfig {
...
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
...
retryTemplate.registerListener(new DefaultListenerSupport());
return retryTemplate;
}
}
7、測試結果
為了完成我們的示例,讓我們驗證一下結果:
@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
8、結論
在本文中,我們看到了如何使用註解、RetryTemplate
和回撥監聽器來使用Spring Retry。
原文地址:https://www.baeldung.com/spring-retry
翻譯:碼農熊貓
更多技術乾貨,請訪問我的個人網站https://pinmost.com,或關注公眾號【碼農熊貓】