1. 介紹
在項目中,調用第三方接口響應比較慢,或者由於網絡抖動等原因,導致無響應的情況,就要用到重試機制.比較簡單成熟的方案就是使用spring-retry功能,spring-retry需要使用aop的特性,所以引入aspectj。
2. 項目依賴
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>
3、spring對於重試機制的實現,給了幾個抽象。
- BackOff:補償值,一般指失敗后多久進行重試的延遲值。
- Sleeper:暫停應用的工具,通常用來應用補償值。
- BackOffPolicy:補償策略,決定失敗后如何確定補償值。
- RetryContext:重試上下文,代表了能被重試動作使用的資源。
- RetryPolicy:重試策略,決定失敗能否重試。
- RecoveryCallback:定義一個動作recover,在重試耗盡后的動作。
- RetryCallback:具體的重試動作。
- RetryOperations:通過傳遞RetryCallback,進行重試操作。
- RetryState:重試狀態,通常包含一個重試的鍵值。
- RetryStatistics和RetryListener,用來監控Retry的執行情況,並生成統計信息。
4、代碼示例
@Retryable(value= {Exception.class}, maxAttempts = 3) public void call() throws Exception { System.out.println("do something..."); throw new Exception("RPC調用異常"); } @Recover public void recover(RemoteAccessException e) { System.out.println(e.getMessage()); }
@Retryable(maxAttempts = 3, backoff = @Backoff(value = 3000, multiplier = 1.5)) public Customer getCustomer(String customerId) { if (true) { JSONArray data = retObj.getJSONArray("data"); if (data != null && !data.isEmpty()) { return data.toJavaList(Customer.class).get(0); } } else { log.error("異常,{}", customerId); throw new RuntimeException("獲數據失敗"); } return null; }
@Retryable被注解的方法發生異常時會重試。
@Retryable注解中的參數說明:
- maxAttempts :最大重試次數,默認為3,如果要設置的重試次數為3,可以不寫;
- value:拋出指定異常才會重試
- include:和value一樣,默認為空,當exclude也為空時,所有異常都重試
- exclude:指定不處理的異常,默認空,當include也為空時,所有異常都重試
- backoff:重試等待策略,默認使用@Backoff@Backoff的value默認為1000L,我們設置為2000L。
@Backoff重試補償機制,默認沒有
@Backoff注解中的參數說明:
- value:隔多少毫秒后重試,默認為1000L,我們設置為3000L;
- delay:和value一樣,但是默認為0;
- multiplier(指定延遲倍數)默認為0,表示固定暫停1秒后進行重試,如果把multiplier設置為1.5,則第一次重試為2秒,第二次為3秒,第三次為4.5秒。
5、@Recover注解
可以在指定方法上標記@Recover來開啟重試失敗后調用的方法(注意,需跟重處理方法在同一個類中)
@Recover:
當重試到達指定次數時,被注解的方法將被回調,可以在該方法中進行日志處理。需要注意的是發生的異常和入參類型一致時才會回調。
6、采坑提示
1、由於retry用到了aspect增強,所有會有aspect的坑,就是方法內部調用,會使aspect增強失效,那么retry當然也會失效。參考改鏈接;
public class demo { public void A() { B(); } //這里B不會執行 @Retryable(Exception.class) public void B() { throw new RuntimeException("retry..."); } }
2、maxAttemps參數解釋的是說重試次數,但是我再打斷點的時候發現這個=1時,方法一共只執行了一次。
3、recover回調報錯
org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method報錯顯示找不到recovery方法
解決recover回調報錯的方案就這這兩句話:
- 異常類型需要與Recover方法參數類型保持一致
- recover方法返回值需要與重試方法返回值保證一致
補充
對於非冪等的請求(比如新增,更新操作),千萬不要使用重試,對數據一致性會造成很大影響。