後端---Spring-Retry框架介紹和基本開發

Spring-Retry框架介紹和基本開發

 

一、背景介紹

   前天是來到公司實習的第五天,之前幾天分給我的任務一直都是熟悉項目,閱讀項目代碼梳理其中某些接口或枚舉類之間的關係,並書寫文檔。直到前天下午組長把我叫來說出了這樣一個需求,在我們項目中有一個認證身份證號的service接口,該接口是調用第三方公司的接口,但是第三方公司的接口並不穩定,每天大約有1.5w個返回錯誤code值20001,但是在我們這個接口,將這個返回視爲認證成功,這樣以來就會造成用戶在下一步進行操作的時候遇到問題。但是因爲對方的接口在大部分情況下仍然是能返回正確的值,所以當對方接口返回20001的時候,我們可以嘗試重試繼續請求,看這樣做是否能降低每天該接口的錯誤率。

二、解決方法

1.簡單的try-catch-redo模式

這裏我們列舉一段代碼來看一下這種方式

public void commonRetry(Map<String, Object> dataMap) throws InterruptedException {
        Map<String, Object> paramMap = Maps.newHashMap();
        paramMap.put("tableName", "creativeTable");
        paramMap.put("ds", "20160220");
        paramMap.put("dataMap", dataMap);
        boolean result = false;
        try {
            result = uploadToOdps(paramMap);
            if (!result) {
                Thread.sleep(1000);
                uploadToOdps(paramMap);  //一次重試
            }
        } catch (Exception e) {
            Thread.sleep(1000);
            uploadToOdps(paramMap);//一次重試
        }
    }

2.try-catch-redo-retry strategy策略重試模式

public void commonRetry(Map<String, Object> dataMap) throws InterruptedException {
        Map<String, Object> paramMap = Maps.newHashMap();
        paramMap.put("tableName", "creativeTable");
        paramMap.put("ds", "20160220");
        paramMap.put("dataMap", dataMap);
        boolean result = false;
        try {
            result = uploadToOdps(paramMap);
            if (!result) {
                reuploadToOdps(paramMap,1000L,10);//延遲多次重試
            }
        } catch (Exception e) {
            reuploadToOdps(paramMap,1000L,10);//延遲多次重試
        }
    }

方案二不過在方案一的基礎上加上了延遲時間重試策略,並沒有太大改動

我們可以看到這樣無論是方案一還是方案二的確能夠解決我們剛纔的問題,但是這樣做的後果是正常的代碼邏輯和重試邏輯強耦合,對正常邏輯預期結果被動重試觸發,對於重試根源往往由於邏輯複雜被淹沒,可能導致後續運維對於重試邏輯要解決什麼問題產生不一致理解。重試正確性難保證而且不利於運維,原因是重試設計依賴正常邏輯異常或重試根源的臆測

2.spring-retry框架模式(

關於spring-retry基本概念和通過重寫類方式的開發,這裏不再重複造輪子,直接去看這篇博客介紹的已經很全面

https://blog.csdn.net/u011116672/article/details/77823867

第一步,在pom.xml文件夾下添加如下依賴

<!-- Spring Retry -->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

第二步,在springBoot項目啓動主類上添加@EnableRetry表示開啓重試機制,也可以使用@EnableRetry(proxyTargetClass=false)表示使用cglib

@SpringBootApplication
@EnableRetry
public class SpringbootRetryApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootRetryApplication.class, args);
    }
}

第三步、在需要重試的方法上加上@Retryable註解

@Service
public class IdCardVerifyRetryService {

    private static final Logger logger = LoggerFactory.getLogger(IdCardVerifyRetryService.class);

    @Autowired
    private VerifyClient verifyClient;

    @Retryable(maxAttempts = 3, backoff = @Backoff(value = 100L), value = RetryException.class)
    public VerifyIdcardVO retryable(VerifyUserInfoParam verifyUserInfoParam) throws Exception {
        logger.error(verifyUserInfoParam.toString());
        logger.error("in retryable function.");
        VerifyIdcardVO verifyIdcardVO = verifyClient.verify(verifyUserInfoParam);
        verifyIdcardVO.setCode(IdCardVerifyResultEnum.SYSTEM_ERROR);
        if (verifyIdcardVO.getCode().equals(IdCardVerifyResultEnum.SYSTEM_ERROR)) {
            logger.error("verify idcard error,starting retry.");
            throw new RetryException("執行verify方法返回IdCardVerifyResultEnum.SYSTEM_ERROR");
        }
        return verifyIdcardVO;
    }
}

 

使用Junit進行單元測試,可看出結果如下: 

三、註解講解和注意事項

基本註解:

@Retryable註解

被註解的方法發生異常時會重試 

  • value:指定發生的異常進行重試 
  • include:和value一樣,默認空,當exclude也爲空時,所有異常都重試 
  • exclude:指定異常不重試,默認空,當include也爲空時,所有異常都重試 
  • maxAttemps:重試次數,默認3 
  • backoff:重試補償機制,默認沒有

@Backoff註解

  • delay:指定延遲後重試 
  • multiplier:指定延遲的倍數,比如delay=5000l,multiplier=2時,第一次重試爲5秒後,第二次爲10秒,第三次爲20秒

@Recover註解 (我這裏沒有使用到@Recover註解,原因見下注意事項)

@CircuitBreaker(沒有使用斷路器):用於方法,實現熔斷模式。 
include 指定處理的異常類。默認爲空 
exclude指定不需要處理的異常。默認爲空 
vaue指定要重試的異常。默認爲空 
maxAttempts 最大重試次數。默認3次 
openTimeout 配置熔斷器打開的超時時間,默認5s,當超過openTimeout之後熔斷器電路變成半打開狀態(只要有一次重試成功,則閉合電路) 
resetTimeout 配置熔斷器重新閉合的超時時間,默認20s,超過這個時間斷路器關閉
 

當重試到達指定次數時,被註解的方法將被回調,可以在該方法中進行日誌處理。需要注意的是發生的異常和入參類型一致時纔會回調。

注意事項:

1、使用了@Retryable的方法不能在本類被調用,不然重試機制不會生效。也就是要標記爲@Service,然後在其它類使用@Autowired注入或者@Bean去實例才能生效。

2、要觸發@Recover方法,那麼在@Retryable方法上不能有返回值,只能是void才能生效。

3、使用了@Retryable的方法裏面不能使用try...catch包裹,要在發放上拋出異常,不然不會觸發。

4、在重試期間這個方法是同步的,如果使用類似Spring Cloud這種框架的熔斷機制時,可以結合重試機制來重試後返回結果。

5、Spring Retry不只能注入方式去實現,還可以通過API的方式實現,類似熔斷處理的機制就基於API方式實現會比較寬鬆。

(尤其是第二條,@Retryable的方法不能在本類那樣直接調用,由於retry用到了aspect增強,所有會有aspect的坑,就是方法內部調用,會使aspect增強失效,那麼retry當然也會失效)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章