1.前言
接口請求重試機制是保證系統穩定性和容錯能力的重要手段之一。當接口請求發生失敗或暫時性錯誤時,通過重試機制可以提高請求的成功率。本文將詳細介紹接口請求重試機制的幾種常見方法。
2.幾種方法
2.1循環重試
它的基本思路是:
-
定義重試次數,如最大重試5次
-
發送請求,如果失敗則進入重試邏輯
-
在循環內部,記錄當前已重試次數,如當前已重試2次
-
判斷當前重試次數是否達到最大次數,如果達到則終止循環,否則進行重試
-
在循環內部,可以添加定時重試間隔,也可以使用指數退避算法
-
發送重試請求,重復判斷是否成功,直到成功、達到最大次數或其他終止條件
示例
public class Retry { private static final int MAX_RETRIES = 5; public static Response request() throws Exception { int retries = 0; while (true) { try { // 發送請求,返回響應 Response response = HttpClient.sendRequest(); // 請求成功則返回響應 if (response.isSuccess()) { return response; } } catch (Exception e) { // 請求失敗進行重試 } // 判斷是否超過最大重試次數 if (++retries >= MAX_RETRIES) { throw new Exception("Exceeded max retries"); } // 增加間隔后重試 int interval = (int) (Math.random() * 1000); Thread.sleep(interval); } } public static void mAIn(String[] args) throws Exception { Response response = request(); // ... } }
2.2 使用Spring Retry庫
使用 Spring Retry 庫可以很方便地實現接口請求的重試機制。
2.2.1 添加 Maven 依賴
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.3.1</version> </dependency>
2.2.2 添加 @EnableRetry 注解啟用重試功能
2.2.3 在需要重試的方法上添加 @Retryable 注解
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 5000)) public User getUser(String id) { // 遠程調用接口 }
@Retryable 定義了重試規則:- value - 重試的異常類型- maxAttempts - 最大重試次數- backoff - 重試等待策略
2.2.4. 還可以自定義 RetryTemplate 進行更復雜的重試控制
RetryTemplate template = new RetryTemplate(); template.execute(context -> { // 可在此處自定義重試邏輯 return remoteClient.invoke(); });
Spring Retry 為接口請求重試提供了完善和易用的解決方案,可以靈活控制各種重試參數,適用于復雜系統的容錯要求。
2.3 并發框架異步重試
使用并發框架的異步請求方式可以較簡單地實現接口請求的重試機制。以CompletableFuture為例:
2.3.1 發送請求使用CompletableFuture封裝:
CompletableFuture<Response> future = CompletableFuture.supplyAsync(() -> { return service.call(); });
2.3.2 當請求失敗時,使用retryAsync自動完成重試:
future = future.exceptionally(e -> { return service.retryAsync(); });
2.3.3 可以鏈式調用,自定義重試邏輯:
future .exceptionally(e -> { // 處理異常 }) .thenApplyAsync(resp -> { // 處理響應 }) .retryAsync(retryCount, delay);
主要優點是:
-
線程安全的異步請求
-
自動重試失敗任務
-
簡潔的鏈式編程方式
-
避免阻塞主線程
使用并發框架可以便捷地實現異步重試機制,提高系統容錯性。其他框架如RxJAVA也有類似的重試機制。
2.4 消息隊列重試
使用消息隊列可以實現接口請求的異步重試機制。
基本思路是:
-
接口請求發送失敗后,將請求信息封裝為消息,發送到請求重試的隊列中。
-
消息消費者從隊列中獲取失敗的請求,根據策略進行重試。
-
重復重試直到成功、重試次數用盡或其他終止條件。
-
成功后將消息移除隊列,失敗則保留消息供再次重試。
主要步驟:
-
創建請求重試隊列,如“request.retry.queue”
-
接口請求失敗后,生成重試消息,發送到隊列
-
消費者啟動線程從隊列中取消息重試
-
根據重試策略進行定時重試或最大重試數
-
成功則確認消息,失敗則重新入隊
使用消息隊列進行重試有利于:
-
異步重試,不阻塞主線程
-
可靠地完成重試任務
-
靈活控制重試策略
示例
// 1. 創建隊列 Queue retryQueue = new Queue("request.retry.queue"); // 2. 請求失敗,發送重試消息 public void request() { try { // 調用接口 httpClient.post(url, payload); } catch (Exception e) { // 發送重試消息 Message msg = new Message(url, payload, maxRetries); retryQueue.send(msg); } } // 3. 消費者線程進行重試 class RetryConsumer implements Runnable { public void run() { while (true) { Message msg = retryQueue.take(); for (int i = 0; i < msg.getMaxRetries(); i++) { try { // 重試請求 httpClient.post(msg.getUrl(), msg.getPayload()); // 請求成功,結束循環 break; } catch (Exception e) { // 等待后繼續重試 } } // 重試完成后,確認消息 retryQueue.confirm(msg); } } }
這就是使用消息隊列實現接口重試的基本流程,可以根據需求擴展重試策略、異常處理等邏輯。
2.5 自定義重試工具類
使用自定義的重試工具類來實現接口請求的重試機制,提高代碼的復用性和可維護性。
重試工具類的實現思路:
-
提供重試方法,參數包括請求函數、重試策略等
-
在重試方法內部執行循環請求
-
每次請求失敗時,根據策略等待一段時間
-
記錄當前重試次數,與最大次數比較
-
請求成功或者達到最大重試次數則結束循環
示例:
public class RetryUtil { public static <T> T retry(RetryCallable<T> callable, RetryPolicy policy) { int retries = 0; while(true) { try { return callable.call(); } catch(Exception e) { if (retries >= policy.maxRetries) { throw e; } // 等待 policy.delay(); // 重試次數加1 retries++; } } } } // 執行請求的函數接口 interface RetryCallable<T> { T call(); } // 重試策略 class RetryPolicy { int maxRetries; int delay; } // 使用示例 RetryUtil.retry(() -> { // 接口請求 return httpClient.get(url); }, policy);
這樣可以提高重試相關邏輯的復用性,避免寫重復代碼。
2.6 使用遞歸結構
使用遞歸結構也可以實現接口請求的重試機制。
基本思路是設計一個遞歸函數,在函數內部發送請求,如果失敗則繼續遞歸調用自身再次重試。
示例:
public class RetryRequest { private static final int MAX_RETRIES = 3; public static Response request(int retries) { try { // 發送請求 Response response = HttpClient.get("http://example.com"); return response; } catch (Exception e) { // 處理異常 // 判斷是否需要重試 if (retries < MAX_RETRIES) { // 增加重試次數 retries++; // 延遲1秒鐘 Thread.sleep(1000); // 遞歸調用自身進行重試 return request(retries); } // 重試失敗 throw new RuntimeException("Request failed after " + MAX_RETRIES + " retries!"); } } public static void main(String[] args) { Response response = request(0); // 處理響應 } }
主要邏輯是通過遞歸不斷調用自身來實現重試。優點是邏輯較簡單清晰,缺點是遞歸層次過深時可能會導致堆棧溢出。需要合理設置最大遞歸深度,也可以通過循環改寫遞歸來避免深層遞歸。
2.7 使用Resilience4j
Resilience4j是一個很好的Java重試庫,可以用它來實現接口請求的重試機制。
主要步驟:
2.7.1添加Resilience4j依賴
<dependency> <groupId>io.Github.resilience4j</groupId> <artifactId>resilience4j-retry</artifactId> </dependency>
2.7.2 定義重試邏輯
RetryConfig config = RetryConfig.custom() .maxAttempts(3) .waitDuration(Duration.ofMillis(500)) .build(); Retry retry = Retry.of("backend", config);
2.7.3 使用重試邏輯調用接口
String result = retry.executeSupplier(() -> { // 發送請求 return backendService.callAPI(); });
2.7.4 自定義重試異常predicate
RetryConfig config = RetryConfig.custom() .retryOnException(e -> isRetryable(e)) .build();
Resilience4j提供了線程安全的重試 decorator,可以通過配置靈活控制重試策略,很好地支持了接口請求重試。
2.8 使用網絡工具重試
我們常用的一些網絡工具來做重試
示例
public class RetryExample { private static final int MAX_RETRIES = 3; public static String request(String url) throws Exception { int retries = 0; while (true) { try { // 使用HttpClient發送請求 return HttpClientUtils.get(url); } catch (Exception e) { if (retries >= MAX_RETRIES) { throw e; } // 增加重試次數 retries++; // 延遲1秒鐘 TimeUnit.SECONDS.sleep(1); } } } public static void main(String[] args) throws Exception { String result = request("http://example.com/api"); System.out.println(result); } } // 網絡工具類 class HttpClientUtils { public static String get(String url) throws IOException { // 發送GET請求并返回結果 } }
主要通過循環和網絡工具類來實現重試邏輯,延時控制也可以用Random來實現指數退避。這種 utilities + 循環 的組合可以實現靈活可復用的重試機制。
3.注意事項
接口請求重試時需要注意以下幾點:
3.1 冪等性接口需要是冪等的,多次調用結果相同,避免重復執行帶來副作用。
3.2 資源競爭重試可能對服務端造成更大壓力,需要考慮限流等措施。
3.3 超時設置合理設置重試最大次數和總超時時間,避免長時間等待。
3.4 重試條件明確哪些異常情況下需要重試,不能無腦重試所有錯誤。
3.5 數據一致性請求成功后要冪等更新狀態,避免重復數據。
3.6 異步機制重試過程不要阻塞主業務線程。
3.7 退避策略失敗后延遲一段時間再重試,可選避免集群重試。
3.8 日志記錄記錄重試的次數、錯誤原因等信息,方便排查問題。
3.9 容錯機制重試失敗后的降級處理,避免級聯失敗。
總結
接口請求重試機制對保證系統高可用非常關鍵,需要根據業務需求選擇合適的重試策略。常用的組合策略包括帶最大次數的定時/指數退避重試、故障轉移重試等。重試機制需要綜合設置以達到容錯效果 又避免產生過大的系統負載。