指數退避的原理是對於連續錯誤響應,重試等待間隔越來越長。
您應該實施最長延遲間隔和最大重試次數。最長延遲間隔和最大重試次數不一定是固定值,並且應當根據正在執行的操作和其他本地因素(例如網絡延遲)進行設置。
大多數指數退避算法會利用抖動(隨機延遲)來防止連續的衝突。
由於在這些情況下您並未嘗試避免此類衝突,因此無需使用此隨機數字。但是,如果使用併發客戶端,抖動可幫助您更快地成功執行請求。
至於指數避退算法的場景有哪些呢?指數退避算法在計算機網絡中應用很廣泛,這裏簡單說兩個場景,第一個場景,接入三方支付服務,在三方支付提供的接入接口規範中,服務方交易結束結果通知和商戶主動查詢交易結果都用到重發機制,這就是所謂的退避算法,這地方其實也引出了另一個知識點——接口的冪等性( 使用相同參數對同一資源重複調用某個接口的結果與調用一次的結果相同),這裏不再過多贅述。第二個場景,在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