最近项目中用到了很多 RPC 调用,其中定义了一个如下的重试策略:
public static final RetryStrategy<Object> DEFAULT_RETRY_STRATEGY =
new ExponentialBackoffAndJitterBuilder()
.retryOn(Calls.getCommonRecoverableThrowables())
.retryOn(RetryableSBAAGTSvcException.class)
.withInitialIntervalMillis(RETRY_INITIAL_INTERVAL_MILLIS)
.withMaxAttempts(RETRY_MAX_ATTEMPTS)
.build();
关于 指数退避抖动 算法 Exponential Backoff And Jitter,参考如下两篇文章:
指数退避的原理是对于连续错误响应,重试等待间隔越来越长。
您应该实施最长延迟间隔和最大重试次数。最长延迟间隔和最大重试次数不一定是固定值,并且应当根据正在执行的操作和其他本地因素(例如网络延迟)进行设置。
大多数指数退避算法会利用抖动(随机延迟)来防止连续的冲突。
由于在这些情况下您并未尝试避免此类冲突,因此无需使用此随机数字。但是,如果使用并发客户端,抖动可帮助您更快地成功执行请求。
以下代码演示如何在 Java 中实施此增量延迟。
public class RetryDemo {
// 最长延迟间隔,单位是毫秒
private static int MAX_WAIT_INTERVAL = 100000;
// 最大重试次数
private static int MAX_RETRIES = 5;
public enum Results {
SUCCESS,
NOT_READY,
THROTTLED,
SERVER_ERROR
}
public static void main(String[] args) {
doOperationAndWaitForResult();
}
// 指数退避 算法
public static void doOperationAndWaitForResult() {
try {
int retries = 0;
boolean retry = false;
do {
long waitTime = Math.min(getWaitTimeExp(retries), MAX_WAIT_INTERVAL);
System.out.print("等待时间:" + waitTime + " ms \n");
// Wait for the result.
Thread.sleep(waitTime);
// Get the result of the asynchronous operation.
Results result = getAsyncOperationResult();
if (Results.SUCCESS == result) {
retry = false;
} else if (Results.NOT_READY == result) {
retry = true;
} else if (Results.THROTTLED == result) {
retry = true;
} else if (Results.SERVER_ERROR == result) {
retry = true;
}
else {
retry = false;
}
} while (retry && (retries++ < MAX_RETRIES));
}
catch (Exception ex) {
}
}
// 假设每次都返回 SERVER_ERROR
public static Results getAsyncOperationResult() {
return Results.SERVER_ERROR;
}
// 根据重试的次数,返回 2 的指数的等待时间,单位是毫秒
public static long getWaitTimeExp(int retryCount) {
long waitTime = ((long) Math.pow(2, retryCount) * 100L);
return waitTime;
}
}
输出如下:
等待时间:100 ms
等待时间:200 ms
等待时间:400 ms
等待时间:800 ms
等待时间:1600 ms
等待时间:3200 ms