'限流算法之漏桶算法、令牌桶算法'

算法 Java 軟件架構 2019-07-17
"

限流

每個API接口都是有訪問上限的,當訪問頻率或者併發量超過其承受範圍時候,我們就必須考慮限流來保證接口的可用性或者降級,即接口也需要安裝上保險絲,以防止非預期的請求對系統壓力過大而引起的系統癱瘓。

通常的策略就是拒絕多餘的訪問,或者讓多餘的訪問排隊等待服務,或者引流。

限流算法

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

漏桶算法

漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然後就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率。示意圖如下:

"

限流

每個API接口都是有訪問上限的,當訪問頻率或者併發量超過其承受範圍時候,我們就必須考慮限流來保證接口的可用性或者降級,即接口也需要安裝上保險絲,以防止非預期的請求對系統壓力過大而引起的系統癱瘓。

通常的策略就是拒絕多餘的訪問,或者讓多餘的訪問排隊等待服務,或者引流。

限流算法

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

漏桶算法

漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然後就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率。示意圖如下:

限流算法之漏桶算法、令牌桶算法

可見這裡有兩個變量,一個是桶的大小,支持流量突發增多時可以存多少的水(burst),另一個是水桶漏洞的大小(rate),偽代碼如下:

double rate; // leak rate in calls/s

double burst; // bucket size in calls

long refreshTime; // time for last water refresh

double water; // water count at refreshTime

refreshWater() {

long now = getTimeOfDay();

//水隨著時間流逝,不斷流走,最多就流乾到0.

water = max(0, water- (now - refreshTime)*rate);

refreshTime = now;

}

bool permissionGranted() {

refreshWater();

if (water < burst) { // 水桶還沒滿,繼續加1

water ++;

return true;

} else {

return false;

}

}

因為漏桶的漏出速率是固定的參數,所以,即使網絡中不存在資源衝突(沒有發生擁塞),漏桶算法也不能使流突發(burst)到端口速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率。

令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣,但方向相反的算法。隨著時間流逝,系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裡加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了。新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務。

"

限流

每個API接口都是有訪問上限的,當訪問頻率或者併發量超過其承受範圍時候,我們就必須考慮限流來保證接口的可用性或者降級,即接口也需要安裝上保險絲,以防止非預期的請求對系統壓力過大而引起的系統癱瘓。

通常的策略就是拒絕多餘的訪問,或者讓多餘的訪問排隊等待服務,或者引流。

限流算法

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

漏桶算法

漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然後就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率。示意圖如下:

限流算法之漏桶算法、令牌桶算法

可見這裡有兩個變量,一個是桶的大小,支持流量突發增多時可以存多少的水(burst),另一個是水桶漏洞的大小(rate),偽代碼如下:

double rate; // leak rate in calls/s

double burst; // bucket size in calls

long refreshTime; // time for last water refresh

double water; // water count at refreshTime

refreshWater() {

long now = getTimeOfDay();

//水隨著時間流逝,不斷流走,最多就流乾到0.

water = max(0, water- (now - refreshTime)*rate);

refreshTime = now;

}

bool permissionGranted() {

refreshWater();

if (water < burst) { // 水桶還沒滿,繼續加1

water ++;

return true;

} else {

return false;

}

}

因為漏桶的漏出速率是固定的參數,所以,即使網絡中不存在資源衝突(沒有發生擁塞),漏桶算法也不能使流突發(burst)到端口速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率。

令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣,但方向相反的算法。隨著時間流逝,系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裡加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了。新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務。

限流算法之漏桶算法、令牌桶算法

大小固定的令牌桶可自行以恆定的速率源源不斷地產生令牌。如果令牌不被消耗,或者被消耗的速度小於產生的速度,令牌就會不斷地增多,直到把桶填滿。後面再產生的令牌就會從桶中溢出。最後桶中可以保存的最大令牌數永遠不會超過桶的大小。

傳送到令牌桶的數據包需要消耗令牌。不同大小的數據包,消耗的令牌數量不一樣。

令牌桶這種控制機制基於令牌桶中是否存在令牌來指示什麼時候可以發送流量。令牌桶中的每一個令牌都代表一個字節。如果令牌桶中存在令牌,則允許發送流量;而如果令牌桶中不存在令牌,則不允許發送流量。因此,如果突發門限被合理地配置並且令牌桶中有足夠的令牌,那麼流量就可以以峰值速率發送。

Guava RateLimiter 簡介

Google開源工具包Guava提供了限流工具類RateLimiter,該類基於令牌桶算法(Token Bucket)來完成限流,非常易於使用。RateLimiter經常用於限制對一些物理資源或者邏輯資源的訪問速率。它支持兩種獲取permits接口,一種是如果拿不到立刻返回false,一種會阻塞等待一段時間看能不能拿到。

RateLimiter和Java中的信號量(java.util.concurrent.Semaphore)類似,Semaphore通常用於限制併發量。

源碼中的一個例子,比如我們有很多任務需要執行,但是我們不希望每秒超過兩個任務執行,那麼我們就可以使用RateLimiter:

final RateLimiter rateLimiter = RateLimiter.create(2.0);

void submitTasks(List<Runnable> tasks, Executor executor) {

for (Runnable task : tasks) {

rateLimiter.acquire(); // may wait

executor.execute(task);

}

}

另外一個例子,假如我們會產生一個數據流,然後我們想以每秒5kb的速度發送出去。我們可以每獲取一個令牌(permit)就發送一個byte的數據,這樣我們就可以通過一個每秒5000個令牌的RateLimiter來實現:

final RateLimiter rateLimiter = RateLimiter.create(5000.0);

void submitPacket(byte[] packet) {

rateLimiter.acquire(packet.length);

networkService.send(packet);

}

另外,我們也可以使用非阻塞的形式達到降級運行的目的,即使用非阻塞的tryAcquire()方法:

if(limiter.tryAcquire()) { //未請求到limiter則立即返回false

doSomething();

}else{

doSomethingElse();

}

漏桶算法和令牌桶算法的選擇

漏桶算法與令牌桶算法在表面看起來類似,很容易將兩者混淆。但事實上,這兩者具有截然不同的特性,且為不同的目的而使用。

漏桶算法與令牌桶算法的區別在於,漏桶算法能夠強行限制數據的傳輸速率,令牌桶算法能夠在限制數據的平均傳輸速率的同時還允許某種程度的突發傳輸。

需要注意的是,在某些情況下,漏桶算法不能夠有效地使用網絡資源,因為漏桶的漏出速率是固定的,所以即使網絡中沒有發生擁塞,漏桶算法也不能使某一個單獨的數據流達到端口速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率。而令牌桶算法則能夠滿足這些具有突發特性的流量。通常,漏桶算法與令牌桶算法結合起來為網絡流量提供更高效的控制。

"

限流

每個API接口都是有訪問上限的,當訪問頻率或者併發量超過其承受範圍時候,我們就必須考慮限流來保證接口的可用性或者降級,即接口也需要安裝上保險絲,以防止非預期的請求對系統壓力過大而引起的系統癱瘓。

通常的策略就是拒絕多餘的訪問,或者讓多餘的訪問排隊等待服務,或者引流。

限流算法

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

漏桶算法

漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然後就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率。示意圖如下:

限流算法之漏桶算法、令牌桶算法

可見這裡有兩個變量,一個是桶的大小,支持流量突發增多時可以存多少的水(burst),另一個是水桶漏洞的大小(rate),偽代碼如下:

double rate; // leak rate in calls/s

double burst; // bucket size in calls

long refreshTime; // time for last water refresh

double water; // water count at refreshTime

refreshWater() {

long now = getTimeOfDay();

//水隨著時間流逝,不斷流走,最多就流乾到0.

water = max(0, water- (now - refreshTime)*rate);

refreshTime = now;

}

bool permissionGranted() {

refreshWater();

if (water < burst) { // 水桶還沒滿,繼續加1

water ++;

return true;

} else {

return false;

}

}

因為漏桶的漏出速率是固定的參數,所以,即使網絡中不存在資源衝突(沒有發生擁塞),漏桶算法也不能使流突發(burst)到端口速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率。

令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣,但方向相反的算法。隨著時間流逝,系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裡加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了。新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務。

限流算法之漏桶算法、令牌桶算法

大小固定的令牌桶可自行以恆定的速率源源不斷地產生令牌。如果令牌不被消耗,或者被消耗的速度小於產生的速度,令牌就會不斷地增多,直到把桶填滿。後面再產生的令牌就會從桶中溢出。最後桶中可以保存的最大令牌數永遠不會超過桶的大小。

傳送到令牌桶的數據包需要消耗令牌。不同大小的數據包,消耗的令牌數量不一樣。

令牌桶這種控制機制基於令牌桶中是否存在令牌來指示什麼時候可以發送流量。令牌桶中的每一個令牌都代表一個字節。如果令牌桶中存在令牌,則允許發送流量;而如果令牌桶中不存在令牌,則不允許發送流量。因此,如果突發門限被合理地配置並且令牌桶中有足夠的令牌,那麼流量就可以以峰值速率發送。

Guava RateLimiter 簡介

Google開源工具包Guava提供了限流工具類RateLimiter,該類基於令牌桶算法(Token Bucket)來完成限流,非常易於使用。RateLimiter經常用於限制對一些物理資源或者邏輯資源的訪問速率。它支持兩種獲取permits接口,一種是如果拿不到立刻返回false,一種會阻塞等待一段時間看能不能拿到。

RateLimiter和Java中的信號量(java.util.concurrent.Semaphore)類似,Semaphore通常用於限制併發量。

源碼中的一個例子,比如我們有很多任務需要執行,但是我們不希望每秒超過兩個任務執行,那麼我們就可以使用RateLimiter:

final RateLimiter rateLimiter = RateLimiter.create(2.0);

void submitTasks(List<Runnable> tasks, Executor executor) {

for (Runnable task : tasks) {

rateLimiter.acquire(); // may wait

executor.execute(task);

}

}

另外一個例子,假如我們會產生一個數據流,然後我們想以每秒5kb的速度發送出去。我們可以每獲取一個令牌(permit)就發送一個byte的數據,這樣我們就可以通過一個每秒5000個令牌的RateLimiter來實現:

final RateLimiter rateLimiter = RateLimiter.create(5000.0);

void submitPacket(byte[] packet) {

rateLimiter.acquire(packet.length);

networkService.send(packet);

}

另外,我們也可以使用非阻塞的形式達到降級運行的目的,即使用非阻塞的tryAcquire()方法:

if(limiter.tryAcquire()) { //未請求到limiter則立即返回false

doSomething();

}else{

doSomethingElse();

}

漏桶算法和令牌桶算法的選擇

漏桶算法與令牌桶算法在表面看起來類似,很容易將兩者混淆。但事實上,這兩者具有截然不同的特性,且為不同的目的而使用。

漏桶算法與令牌桶算法的區別在於,漏桶算法能夠強行限制數據的傳輸速率,令牌桶算法能夠在限制數據的平均傳輸速率的同時還允許某種程度的突發傳輸。

需要注意的是,在某些情況下,漏桶算法不能夠有效地使用網絡資源,因為漏桶的漏出速率是固定的,所以即使網絡中沒有發生擁塞,漏桶算法也不能使某一個單獨的數據流達到端口速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率。而令牌桶算法則能夠滿足這些具有突發特性的流量。通常,漏桶算法與令牌桶算法結合起來為網絡流量提供更高效的控制。

限流算法之漏桶算法、令牌桶算法

"

相關推薦

推薦中...