淺談指數退避演算法

Martin Zhao發表於2016-03-17

今天簡單跟大家聊下指數退避演算法(Exponential Backoff ),關於指數避退演算法的話題開始前首先向大家丟擲幾個問題:指數退避演算法是什麼呢?為什麼要用指數退避演算法呢?指數退避演算法的應用場景有哪些呢?程式碼如何實現呢?帶著這些疑問諸君且向下看。

指數退避演算法到底是什麼呢?wiki上有這麼一段解釋:"Exponential backoff is an algorithm that uses feedback to multiplicatively decrease the rate of some process, in order to gradually find an acceptable rate"。通俗點說, 退避演算法就是網路上的節點在傳送資料衝突後,等待一定時間後再發,等待時間是隨指數增長,從而避免頻繁的觸發衝突。在計算機網路中,二進位制指數退避演算法或截斷指數退避演算法常常作為避免網路堵塞的一部分用於同一資料塊的重發策略。發生n次衝突後,等待時間在0~2^n-1個間隙時間(slot times) 之間選擇隨機選擇。比如,發生第一次衝突後,每個傳送方將會等待0或1個間隙時間(slot times);第二次衝突後,每個傳送方的等待時間將會在0到3個間隙時間(slot times) 之間任意選擇;第三次衝突後,每個傳送方的等待時間將會在0到7個間隙時間(slot times) 之間任意選擇,以此類推,隨著衝突次數的增加,傳送方的等待時間將會有成倍增加的可能性。而從“截斷( truncated)”的字面意思我們不妨可以猜出,到一定次數,指數運算會停止,也就是說等待時間不會再無限的增加下去。比如,設定上限n=10,則最長等待時間為1023個間隙時間。因為在等待時間內某些場景同樣有衝突發生的可能性,所以一般流程會在16次重試後終止。具體的退避演算法如下:

  1. 確定基本退避時間,它就是爭用期(匯流排上的單程端到端傳播時延記為 x,乙太網的端到端往返時間2x)。乙太網把爭用期定為51.2us。對於10Mb/s乙太網,在爭用期內可傳送512bit,即64位元組。也可以說爭用期是512位元時間。1位元時間就是傳送1位元所需要的時間。所以這種時間單位與資料率密切相關。

  2. 從離散的整數集合[0,1,…,]中隨機取出一個數,記為r。重傳應推後的時間就是r倍的爭用期。上面的引數k按下面的公式計算: k=Min[重傳次數,10] 可見當重傳次數不超過10時,引數k等於重傳次數;但當重傳次數超過10時,k就不在增大而一直等於10。

  3. 當重傳達16次仍不能成功時(這表明同時打算髮送的資料站太多,以致連續發生衝突),則丟棄該,並向高層報告。例如,在第1次重傳時,k=1,隨機數r從整數{0,1}中選一個數。因此重傳推遲的時間是0或爭用期,在這兩個時間中隨機選擇一個。若再發生碰撞,則重傳時,k=2,隨機數r就從整數{0,1,2,3}中選一個數。因此重傳推遲的時間是在0,2x ,4x和6x 這4個時間中隨機抽取一個。同樣,若在發生碰撞,則重傳時k=3,隨機數r就從整數{0,1,2,3,4,5,6,7}中選一個數。以此類推。若連續多次發生衝突,就表明可能有較多的站參與爭用通道。但使用退避演算法可使重傳需要推遲的平均時間隨重傳次數而增大(這也稱為動態退避),因而減小發生碰撞的概率,有利於整個系統的穩定。

相信讀到這裡,大家對第二個問題的答案也呼之欲出了吧, 大多數指數退避演算法會利用抖動(隨機延遲)來防止連續的衝突。 但是,如果使用併發客戶端,抖動可幫助您更快地成功執行請求。

至於指數避退演算法的場景有哪些呢?指數退避演算法在計算機網路中應用很廣泛,這裡簡單說兩個場景,第一個場景,接入三方支付服務,在三方支付提供的接入介面規範中,服務方交易結束結果通知和商戶主動查詢交易結果都用到重發機制,這就是所謂的退避演算法,這地方其實也引出了另一個知識點——介面的冪等性( 使用相同引數對同一資源重複呼叫某個介面的結果與呼叫一次的結果相同),這裡不再過多贅述。第二個場景,在app應用中,很多場景會遇到輪詢一類的問題,一般的輪詢對於app效能和電量的消耗都是個巨大的災難。那如何解決這種問題呢?app在上一次更新操作之後還未被使用的情況下,使用指數退避演算法來減少更新頻率,從而節省資源和減少電的消耗。

最後一個問題,這裡簡單的用虛擬碼和java程式碼的方式給大家演示一下增量延遲輪詢的實現方法。

虛擬碼

Do some asynchronous operation.·
retries = 0
DO
    wait for (2^retries * 100) milliseconds
    status = Get the result of the asynchronous operation.
    IF status = SUCCESS
        retry = false
    ELSE IF status = NOT_READY
        retry = true
    ELSE IF status = THROTTLED
        retry = true
    ELSE
        Some other error occurred, so stop calling the API.
        retry = false
    END IF
    retries = retries + 1
WHILE (retry AND (retries < MAX_RETRIES))

java程式碼

public enum Results {
    SUCCESS, 
    NOT_READY, 
    THROTTLED, 
    SERVER_ERROR
}

/*
 * Performs an asynchronous operation, then polls for the result of the
 * operation using an incremental delay.
 */
public static void doOperationAndWaitForResult() {
    try {
        // Do some asynchronous operation.
        long token = asyncOperation();
        int retries = 0;
        boolean retry = false;
        do {
            long waitTime = Math.min(getWaitTimeExp(retries), MAX_WAIT_INTERVAL);
            System.out.print(waitTime + "\n");
            // Wait for the result.
            Thread.sleep(waitTime);
            // Get the result of the asynchronous operation.
            Results result = getAsyncOperationResult(token);
            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 {
                // Some other error occurred, so stop calling the API.
                retry = false;
            }
        } while (retry && (retries++ < MAX_RETRIES));
    }
    catch (Exception ex) {
    }
}

/*
 * Returns the next wait interval, in milliseconds, using an exponential
 * backoff algorithm.
 */
public static long getWaitTimeExp(int retryCount) {
    long waitTime = ((long) Math.pow(2, retryCount) * 100L);
    return waitTime;
}

ok,就談到這啦,源於自己認知的侷限性,也許談的一些地方會有錯誤之處,歡迎大牛指正。

擴充閱讀:

https://en.wikipedia.org/wiki/Exponential_backoff

https://www.awsarchitectureblog.com/2015/03/backoff.html

開啟手機看微信,加微信公眾平臺“Martin說”,每天收穫更多Martin的閒扯。

原文出處:HugNew » 淺談指數退避演算法

相關文章