重试机制
V1.0 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 ID
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();
request.setSysRetryPolicy(retryPolicy);
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 中
}
}
}
}