常見的限流算法


限流:通過對並發訪問/請求進行限速,或者對一個時間窗口內的請求進行限速來保護系統,一旦達到限制速率則可以拒絕服務、排隊或等待、降級等處理

1、計數法(固定時間窗口限流算法):

選定一個時間的起點,之后每當有接口請求到來,我們就將計數器加1,如果在當前時間窗口內,根據限流規則(每秒鍾允許100次訪問請求),出現累加訪問次數超過限流值情況,我們請拒絕后續訪問請求。當進入下一個時間窗口后,計數器就清零重新計數。

缺點:限流策略過於粗略,無法應對兩個時間窗口臨界時間內的突發流量

2、滑動時間窗口限流算法:

在任意1s的時間窗口內,接口的請求次數都不能大於K次。

維護一個K+1的循環隊列,用來記錄1s內到來的請求,【當隊列滿時,tail指向的位置實際上是沒有存儲數據的,所以循環隊列會浪費一個數組的存儲空間】

當有新的請求到來時,我們將與這個新請求的時間間隔超過1s的請求,從隊列中刪除。然后我們再來看循環隊列中是否有空閑位置。如果有,則把新請求存儲在隊列尾部,如果沒有,則說明1s內的請求次數已經超過了限流值K,所以這個請求被拒絕服務。

缺點:只能在選定時間粒度上限流,對選定時間粒度內的更加細粒度的訪問頻率不做限制。

循環隊列代碼:

/**
 * 隊空條件 head == tail
 * 隊滿條件 (tail + 1)% n == head
 * 當隊列滿時,tail指向的位置實際上是沒有存儲數據的,所以循環隊列會浪費一個數組的存儲空間。
 */
public class CircularQueue {

    private String[] items;
    private int n; //隊列大小
    private int head = 0;
    private int tail = 0;

    public CircularQueue(int capacity) {
        items = new String[capacity];
        this.n = capacity;
    }



    public boolean enqueue(String item) {
        //隊列滿了
        if ((tail + 1) % n == head) return false;
        items[tail] = item;
        tail = (tail + 1) % n;
        return true;
    }

    public String dequeue() {
        if (head == tail) return null; //head == tail 隊列是空
        String ret = items[head];
        head = (head + 1) % n;
        return ret;

    }
}  

常用的更平滑的限流算法:漏桶算法和令牌桶算法。

3、漏桶算法:

水(請求)先進入到漏桶里,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然后就拒絕請求可以看出漏桶算法能強行限制數據的傳輸速率。

缺點:對於突發的流量缺乏效率。

4、令牌桶:Google開源項目Guava中的RateLimiter使用的就是令牌桶控制算法。

系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶里加入Token,如果桶已經滿了就不再加了。新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務。

好處:允許流量一定程度的突發。

可以方便的改變速度. 一旦需要提高速率,則按需提高放入桶中的令牌的速率. 一般會定時(比如100毫秒)往桶中增加一定數量的令牌, 有些變種算法則實時的計算應該增加的令牌的數量


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM