Spring框架中一個有用的小組件:Spring Retry

一、概述

Spring Retry 是Spring框架中的一個組件,
它提供了自動從新調用失敗操做的能力。這在錯誤多是暫時發生的(如瞬時網絡故障)的狀況下頗有幫助。java

在本文中,咱們將看到使用Spring Retry的各類方式:註解、RetryTemplate以及回調。git

二、Maven依賴

讓咱們首先將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

要在應用程序中啓用Spring Retry,咱們須要將@EnableRetry註釋添加到咱們的@Configuration類:網絡

@Configuration
@EnableRetry
public class AppConfig { ... }

四、使用Spring Retry

4.一、@Retryable而不用恢復

咱們可使用@Retryable註解爲方法添加劇試功能:框架

@Service
public interface MyService {
    @Retryable(value = RuntimeException.class)
    void retryService(String sql);

}

在這裏,當拋出RuntimeException時嘗試重試。maven

根據@Retryable的默認行爲,重試最多可能發生3次,重試之間有1秒的延遲。ide

4.二、@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.三、自定義@Retryable的行爲

爲了自定義重試的行爲,咱們可使用參數maxAttemptsbackoff

@Service
public interface MyService {
    @Retryable( value = SQLException.class, 
      maxAttempts = 2, backoff = @Backoff(delay = 100))
    void retryServiceWithCustomization(String sql) throws SQLException;
}

這樣最多將有兩次嘗試和100毫秒的延遲。

4.四、使用Spring Properties

咱們還能夠在@Retryable註解中使用properties。

爲了演示這一點,咱們將看到如何將delaymaxAttempts的值外部化到一個properties文件中。

首先,讓咱們在名爲retryConfig.properties的文件中定義屬性:

retry.maxAttempts=2
retry.maxDelay=100

而後咱們指示@Configuration類加載這個文件:

@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }
// ...

最後,咱們能夠在@Retryable的定義中注入retry.maxAttemptsretry.maxDelay的值:

@Service 
public interface MyService { 
  @Retryable( value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
            backoff = @Backoff(delayExpression = "${retry.maxDelay}")) 
  void retryServiceWithExternalizedConfiguration(String sql) throws SQLException; 
}

請注意,咱們如今使用的是maxAttemptsExpressiondelayExpression而不是maxAttemptsdelay

五、RetryTemplate

5.一、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.二、RetryTemplate配置

RetryTemplateRetryOperations的一個實現。

讓咱們在@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.三、使用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.一、添加回調

回調在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);
    }
}

openclose的回調在整個重試以前和以後執行,而onError應用於單個RetryCallback調用。

6.二、註冊監聽器

接下來,咱們將咱們的監聽器(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;
        });
    }
}

從測試日誌中能夠看出,咱們已經正確配置了RetryTemplateRetryListener

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,或關注公衆號【碼農熊貓】

相關文章
相關標籤/搜索