原版 Java SDK 重試機制介紹。
新增重試機制及流控策略(基於流控策略的優雅退避機制)。
核心庫 aliyun-java-sdk-core 從 4.6.0 版本開始支援重試機制,以及提供基於流控策略的優雅退避方案(基於流控策略的優雅退避機制)。Maven 依賴如下:
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.6.0</version>
</dependency>重試機制
關閉重試
預設不開啟重試,您也可以主動配置重試策略為 None,有以下兩種方式:
// Client 層級配置,關閉重試
client.setSysRetryPolicy(RetryPolicy.none());
// Request 層級配置重試策略,優先順序大於 Client 層級
request.setSysRetryPolicy(RetryPolicy.none());重試策略
重試策略提供三種:
指定異常(Exception)
指定狀態代碼(Http StatusCode)
指定服務端返回頭(Http Response Headers)
三種方式互不影響,即三種之間是或關係,觸發任何一種 State 則進行重試或者不重試。也就是說,策略可以是觸發重試的策略也可以是限制重試的策略。
觸發重試和限制重試參考以下配置方式:
建立重試策略集合:
Set<RetryCondition> retryConditions = new HashSet<RetryCondition>(); // Condition 舉例 // 觸發重試的 statusCode 配置,例:當狀態代碼返回 500 或 501 時進行重試 Set<Integer> statusCodes = new HashSet<Integer>(); statusCodes.add(500); // http statusCode statusCodes.add(501); // http statusCode // 加入到觸發重試策略中 retryConditions.add(StatusCodeCondition.create(statusCodes)); // 觸發重試的 exception 配置,例:當遇到 SocketTimeoutException 或 IOException 時進行重試 Set<Class<? extends Exception>> exceptions = new HashSet<Class<? extends Exception>>(); exceptions.add(SocketTimeoutException.class); // exception exceptions.add(IOException.class); // exception // 加入到觸發重試策略中 retryConditions.add(ExceptionsCondition.create(exceptions));建立限制重試集合:
Set<RetryCondition> throttlingConditions = new HashSet<RetryCondition>(); // Condition 舉例 // 限制重試的 statusCode 配置,例如:當狀態代碼返回 429 時禁止進行重試 Set<Integer> code = new HashSet<Integer>(); code.add(429); // http statusCode,限制策略,此處表示遇到 429 則限制重試 // 加入到限制重試策略中 throttlingConditions.add(StatusCodeCondition.create(code));最後將 Conditions 配置到 RetryPolicy 中:
RetryPolicy retryPolicy = RetryPolicy.builder() .maxNumberOfRetries(3) // 最大重試次數 .maxDelayTimeMillis(20 * 1000) // 最大稍候再試時間,單位為 ms,超過這個時間則不再重試 .retryConditions(retryConditions) // 觸發重試策略 .throttlingConditions(throttlingConditions) // 限制重試策略 .build();
其中每次重試的間隔時間,則是根據指數退避演算法計算出,也就是使用的 EqualJitter 演算法計算出下次重試等待時間。
重試 Conditions 高階設定
官方提供了三種 Condition,這裡做更加詳細的介紹。
StatusCodeCondition
存放的是整數集合,根據集合中的 HTTP 狀態代碼,與此次調用的返回的真實的狀態代碼做比較,然後判斷是否進行重試或者限制重試。
ExceptionsCondition
存放的是 Exception 集合,根據集合中的 Exception 類型,與此次調用的返回的拋出的異常做比較,然後判斷是否進行重試或者限制重試。
HeadersCondition
存放的是個 Map,該結構稍微複雜,Map 的 key 值匹配的是返回頭 Headers 的 key 值,Map 的 value 需要實現 Pattern(com.aliyuncs.policy.retry.pattern.Pattern) 介面,表示對 Headers 相應 key 對應的 value 做運算式匹配,例如:包含某個字串、等於某個數值等。
官方實現了兩個 Pattern,一個是 AliyunThrottlingPattern(基於阿里雲的流控策略,基於流控策略的優雅退避機制),另一個是 SimplePattern(直接比較 value 字串是否相等)
可自訂實現 Pattern 介面,其中三個函數需要實現:
meetState(),表示匹配上的原則;
escapeTime(),表示逃脫時間,僅用於限制重試策略上,若該值不等於 -1,則表示在該時間內不進行重試,但也不會直接限制重試,而是等待到可以重試時,再進行重試。若該值大於最大稍候再試時間,則直接返回不再等待。
readFormHeadersContent(String content),這個函數表示給待匹配的值進行賦值,可直接摘抄 SimplePattern 類的實現。
自訂 Condition
使用者可實現自己的 Condition,只需要實現 RetryCondition 介面,需要實現兩個介面函數:
meetState(RetryPolicyContext var1),根據上下文判斷是否符合匹配狀態;
escapeTime(RetryPolicyContext var1),計算逃脫時間,僅用於限制重試策略上,觸發重試策略則預設 -1。
完整程式碼範例
程式碼範例:
package com.aliyun.sample;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.exceptions.ThrottlingException;
import com.aliyuncs.policy.retry.RetryPolicy;
import com.aliyuncs.policy.retry.conditions.ExceptionsCondition;
import com.aliyuncs.policy.retry.conditions.RetryCondition;
import com.aliyuncs.policy.retry.conditions.StatusCodeCondition;
import com.aliyuncs.profile.DefaultProfile;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.HashSet;
import java.util.Set;
public class Sample {
public static void main(String[] args) {
// 建立DefaultAcsClient執行個體並初始化
DefaultProfile profile = DefaultProfile.getProfile(
// 地區ID
"cn-hangzhou",
// 從環境變數擷取RAM使用者的AccessKey ID
System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
// 從環境變數擷取RAM使用者的AccessKey SECRET
System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
IAcsClient client = new DefaultAcsClient(profile);
// Client 層級配置重試策略
client.setSysRetryPolicy(RetryPolicy.none());
// 以CommonRequest為樣本,當前配置亦適用於產品SDK,即<APIName>Request
CommonRequest request = new CommonRequest();
// Request 層級配置重試策略,優先順序大於 Client 層級
request.setSysRetryPolicy(RetryPolicy.defaultRetryPolicy(true));
// 觸發重試的 statusCode 配置
Set<RetryCondition> retryConditions = new HashSet<RetryCondition>();
Set<Integer> statusCodes = new HashSet<Integer>();
statusCodes.add(500); // http statusCode
statusCodes.add(501); // http statusCode
retryConditions.add(StatusCodeCondition.create(statusCodes));
// 觸發重試的 exception 配置
Set<Class<? extends Exception>> exceptions = new HashSet<Class<? extends Exception>>();
exceptions.add(SocketTimeoutException.class); // exception
exceptions.add(IOException.class); // exception
retryConditions.add(ExceptionsCondition.create(exceptions));
// 限制重試的 statusCode 配置
Set<RetryCondition> throttlingConditions = new HashSet<RetryCondition>();
Set<Integer> code = new HashSet<Integer>();
code.add(429); // http statusCode,限制策略,此處表示遇到 429 則限制重試
throttlingConditions.add(StatusCodeCondition.create(code));
RetryPolicy retryPolicy = RetryPolicy.builder()
.maxNumberOfRetries(3) // 最大重試次數
.maxDelayTimeMillis(20 * 1000) // 最大稍候再試時間,超過這個時間則不再重試
.retryConditions(retryConditions) // 重試觸發策略
.enableAliyunThrottlingControl(true) // 使用阿里雲流控策略進行控制
.throttlingConditions(throttlingConditions) // 也可以自己寫限制策略
.build();
try {
// 以CommonRequest為樣本,當前配置亦適用於產品SDK,即<APIName>Request
CommonResponse response = client.getCommonResponse(request);
System.out.println(response.getData());
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
if (ThrottlingException.class.isAssignableFrom(e.getCause().getClass())) {
// 流控異常包在 ClientException 中
}
}
}
}