指数退避的原理是对于连续错误响应,重试等待间隔越来越长。
您应该实施最长延迟间隔和最大重试次数。最长延迟间隔和最大重试次数不一定是固定值,并且应当根据正在执行的操作和其他本地因素(例如网络延迟)进行设置。
大多数指数退避算法会利用抖动(随机延迟)来防止连续的冲突。
由于在这些情况下您并未尝试避免此类冲突,因此无需使用此随机数字。但是,如果使用并发客户端,抖动可帮助您更快地成功执行请求。
至于指数避退算法的场景有哪些呢?指数退避算法在计算机网络中应用很广泛,这里简单说两个场景,第一个场景,接入三方支付服务,在三方支付提供的接入接口规范中,服务方交易结束结果通知和商户主动查询交易结果都用到重发机制,这就是所谓的退避算法,这地方其实也引出了另一个知识点——接口的幂等性( 使用相同参数对同一资源重复调用某个接口的结果与调用一次的结果相同),这里不再过多赘述。第二个场景,在app应用中,很多场景会遇到轮询一类的问题,一般的轮询对于app性能和电量的消耗都是个巨大的灾难。那如何解决这种问题呢?app在上一次更新操作之后还未被使用的情况下,使用指数退避算法来减少更新频率,从而节省资源和减少电的消耗。
以下代码演示如何在 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
以下代码演示如何在Spring中实现退避(指数时间间隔)。
public static void main(String[] args) {
long initialInterval = 100;//初始间隔
long maxInterval = 8 * 1000L;//最大间隔
long maxElapsedTime = 50 * 1000L;//最大时间间隔
double multiplier = 1.5;//递增倍数(即下次间隔是上次的多少倍)
ExponentialBackOff backOff = new ExponentialBackOff(initialInterval, multiplier);
backOff.setMaxInterval(maxInterval);
//currentElapsedTime = interval1 + interval2 + ... + intervalN;
backOff.setMaxElapsedTime(maxElapsedTime);
BackOffExecution execution = backOff.start();
for(int i = 1; i <= 18; i++) {
if(execution.nextBackOff() == BackOffExecution.STOP){
break;
}
System.out.println("i: " + i + ", wait time: " + execution.nextBackOff());
}
}
输入如下:
i: 1, wait time: 150
i: 2, wait time: 337
i: 3, wait time: 757
i: 4, wait time: 1702
i: 5, wait time: 3829
i: 6, wait time: 8000
i: 7, wait time: 8000
i: 8, wait time: 8000
以下代码演示如何在Spring中实现退避(固定时间间隔)。
public static void main(String[] args) {
long initialInterval = 100;//初始间隔
long maxAttempts = 10;
BackOff backOff = new FixedBackOff(initialInterval, maxAttempts);
BackOffExecution execution = backOff.start();
for(int i = 1; i <= 10; i++) {
if(execution.nextBackOff() == BackOffExecution.STOP){
break;
}
System.out.println("i: " + i + ", wait time: " + execution.nextBackOff());
}
}
输入如下:
i: 1, wait time: 100
i: 2, wait time: 100
i: 3, wait time: 100
i: 4, wait time: 100
i: 5, wait time: 100
来源:CSDN
作者:Mr_EvanChen
链接:https://blog.csdn.net/Mr_EvanChen/article/details/102757925