視訊介紹限流演算法,分析漏桶演算法和令牌演算法的應用場景,演算法原理和演算法實現方法
不想看文字的,就來看視訊: 《8分鐘看懂限流演算法》https://www.bilibili.com/video/av26350725
你好,我是好剛,這一講我們來了解限流演算法 (Rate Limiting Throttling)。
1. 應用場景
首先我們看一個典型的應用場景,比如在商品搶購的場景裡面,可能會有百萬級別的使用者請求我們的搶購介面。
這個時候如果不做任何保護措施,伺服器就會承受很大的處理壓力,請求量很高,伺服器負載也很高,並且當請求超過伺服器承載極限的時候,系統就會崩潰,導致所有人都不能訪問。為了保證搶購服務的可用性,一個常用的辦法是對秒殺請求進行限流,攔截掉大部分請求,只允許一部分請求真正進入後端伺服器,這樣就可以防止大量請求造成系統壓力過大導致的系統崩潰,從而保護服務正常可用。這裡限流的常用演算法有漏桶演算法和令牌桶演算法。
2. 漏桶演算法
我們先來看漏桶演算法(Leaky Bucket),先想象有一個木桶,新請求就像水滴一樣,不斷地滴進來,水滴進來的速度是不確定的,有時會快一點,有時會慢一點,同時桶底下有個洞,可以按照固定的速度把水漏走,如果水進來的速度比漏走的快,桶就會滿了,桶滿了水就會漫出來,對應的就是拒絕請求。
漏桶演算法的主要特點是可以平滑網路上的突發流量,請求可以被整形成穩定的流量。
演算法虛擬碼如下:
C // 水桶總容量
r // 漏水速度
at // 上一個請求時間
w // 當前桶裡面的水量
when (b):
bt = now();
wb = (bt - at) * r // 已經流出的水
w = max(w - wb, 0) // 桶裡面的水量減去流走的水量等於當前水量,最多流乾等於0
if w < C: // 水桶還沒滿,可以繼續新增
w ++;
return true
else:
return false
複製程式碼
3. 令牌桶演算法
我們再看下令牌桶演算法(Token Bucket)。也是先有一個木桶,系統按照固定速度,往桶裡加入Token,如果桶已經滿了就不再新增。當有請求到來時,會各自拿走一個Token,取到Token 才能繼續進行請求處理,沒有Token 就拒絕服務。
這裡如果一段時間沒有請求時,桶內就會積累一些Token,下次一旦有突發流量,只要Token 足夠,也能一次處理。所以令牌桶演算法的特點是允許突發流量。
我們看一個例子,看看令牌桶如何允許突發流量,假如令牌則按照每秒5 個的速度放入令牌桶,桶中最多存放20 個令牌,那系統可以支援兩種型別的請求流量,一種是允許持續的每秒處理5 個請求,第二種是每隔4 秒,等桶中20 個令牌攢滿後,就可以處理一次有20 個請求的突發情況。
演算法虛擬碼如下:
C // 水桶總容量
r // 新增令牌速度
at = // 上一個請求時間
w // 當前令牌數
when (b):
bt = now()
wb = (bt - at) * r // 兩次請求期間新增的Token
w = min(w + wb, C) // 上一個請求剩餘的Token 數加上新增的剩餘數不能超過木桶的總容量
if (w > 1.0):
w -- // 令牌足夠,可以處理請求並且將令牌數減一
return true
else:
return false
複製程式碼
4. 兩種演算法比較
最後我們對比下漏桶演算法和令牌桶演算法。其實在實現上,兩種演算法是效果一樣但方向相反的演算法。
漏桶演算法是請求流入的速度不確定,有時快有時慢,是存在突發情況的;但是請求流出的速度是固定的,它是流入會有突發情況,但是流出速度固定。
令牌桶演算法就是固定的Token 流入速度,一個Token 代表一個請求可以被處理的機會;當系統有一段空閒時間之後,桶內有足夠的token,這樣可以處理突發的請求流量,它是流入速度固定,但是流出不固定。
總結下特點:漏桶演算法因為流出速度固定,可以用來整流,無論你流入速率多大,我都按照固定的速度去處理。令牌桶演算法的特點則是,支援突發情況,兩種演算法在實際使用時,應該根據具體場景靈活選用。
限流演算法就介紹到這,我是好剛,好剛用在刀刃上。如果講解對你有幫助,那就請你幫忙轉發吧。
5. 令牌通演算法實現
PHP 實現
//速度 桶大小 / 時間段
$rate = $maxRequests / $period;
$t_key = $keyTime($id); //最後一次獲取令牌時間
$a_key = $keyAllow($id); //已有令牌數
//判斷是否有最後一次獲取令牌記錄
if ($cache->exists($t_key)) {
$c_time = time();
//計算上一次獲取令牌到現在過去的時間
$time_passed = $c_time - $cache->get($t_key);
$cache->set($t_key, $c_time, $ttl);
//獲取桶中令牌數
$allow = $cache->get($a_key);
$allow += $time_passed * $rate; //加上最後一次消費令牌到現在期間增長的令牌數
//令牌數不能超過最大數
if ($allow > $maxRequests) {
$allow = $maxRequests;
}
//使用的令牌數不能超過最大限制
if ($allow < $use) {
$cache->set($a_key, $allow, $ttl);
return 0;
} else {
//消費令牌
$cache->set($a_key, $allow - $use, $ttl);
return (int) ceil($allow);
}
} else {
//記錄當前時間為最後一次處理時間,用於下次使用
$cache->set($t_key, time(), $ttl);
//沒有令牌時按照最大令牌數處理
$cache->set($a_key, $maxRequests - $use, $ttl);
return $maxRequests;
}
複製程式碼
參考資料
- Token bucket wiki
- en.wikipedia.org/wiki/Leaky_…
- PHP Rate Limiting Library With Token Bucket Algorithm
- Rate Limiter
- www.cnblogs.com/haoxinyue/p…
- github.com/yangwenmai/…
- https://blog.52itstyle.com/archives/2982/