在開發具有較高併發訪問的系統時,大家知道的有哪些手段可以保護後端應用不被請求擊垮?比如我們可以分析出有哪些服務的訪問量較大,採用緩衝的方式提供數據。另外,目前分佈式服務框架,例如Dubbo、以及Spring Cloud都提供了服務的治理功能,採用服務降級策略,可以提供Mock值的方式保護服務。當然,上面的辦法明顯時犧牲了用戶體驗,保全應用的做法,對有些場景的服務,使用這種做法是可行的。但是對於某些對服務資源要求非常嚴格的系統中,例如搶票、秒殺、下單購買等場景下,需要給用戶明確的反饋才是最重要的,要麼成功,要麼失敗。
今天我們主要介紹下限流算法:
令牌桶
漏桶
滑動窗口
計數器
令牌桶
令牌桶算法有一個容量固定的令牌桶,該桶可以按照固定的速度生產Token。根據限流的方式不同,消耗令牌的方式也不同。
例1:我們對API進行限流,防止爬蟲或者異常刷接口,我們限的是對API的調用,因此一次API調用,消耗一個token。
例2:我們還可以根據請求的數據包進行限流,通過保護服務的帶寬保護服務的安全。我們可以認為一個字節的請求數據包對應一個token。
令牌桶算法具有如下特性:
算法可以設置生產速率,例如2r/s,這樣500ms就會有一個token產生。
桶具有上限,滿了之後,新生成的token被丟棄。
消耗token時,token不足無法消耗。
容易有洪峰(令牌滿的時候)。
代碼示例:
RateLimiter limiter = RateLimiter.create(10.0); // 每秒不超過10個任務被提交
for (int i = 0; i < 10; i++) {
limiter.acquire(); // 請求RateLimiter, 超過permits會被阻塞
System.out.println("call execute.." + i);
}
漏桶
漏桶和沙漏似的,一個計量工具,具有固定的速率控制。
漏桶具有固定流速。
可空,不流出。
漏桶進入速度隨意。
容量固定,滿則溢。
令牌桶是按照固定速率向桶裡面添加令牌,請求是否被處理要看桶中是否有足夠的令牌。流通是按照固定的流速漏出請求流量。令牌桶允許突發請求,漏桶則平滑請求。
滑動窗口
在TCP通信中,使用了滑動窗口做擁塞控制。需要雙方參與:發送方和接收方。簡單來說,就是接收方要告訴發送方,你不要發的太快,多了我接收不了。發送方為了把數據安全送達,當然要聽接收方的建議。這種限流需要有對應的算法支持。
計數器
有時候我們可以使用簡單的計數器實現限流。我們可以限制一定時間內的訪問併發數,這樣簡單粗暴。例如我們設定一定時間內的連接數不能高於100,數據庫連接數、線程創建數等。這種方式不夠平滑,但可以應對簡單的服務限流場景,實際上我們可以結合滑動時間主動失效並轉移計數的起點,用可以實現。
總結:
限流只是保護系統安全的方式一種手段而已,可以嵌套在應用中,也可以在代理上做入口限流。nginx使用Lua腳本做。後續慢慢說明,這些比較簡單。 實際上我們在應用中,用的比較多的還是令牌桶和漏桶兩種算法,在Google的Guava庫中有對應的實現。
閱讀更多 吳濤分享 的文章