一、問(wèn)題描述
某天A君突然發(fā)現(xiàn)自己的接口請(qǐng)求量突然漲到之前的10倍,沒(méi)多久該接口幾乎不可使用,并引發(fā)連鎖反應(yīng)導(dǎo)致整個(gè)系統(tǒng)崩潰。如何應(yīng)對(duì)這種情況呢?生活給了我們答案:比如老式電閘都安裝了保險(xiǎn)絲,一旦有人使用超大功率的設(shè)備,保險(xiǎn)絲就會(huì)燒斷以保護(hù)各個(gè)電器不被強(qiáng)電流給燒壞。同理我們的接口也需要安裝上“保險(xiǎn)絲”,以防止非預(yù)期的請(qǐng)求對(duì)系統(tǒng)壓力過(guò)大而引起的系統(tǒng)癱瘓,當(dāng)流量過(guò)大時(shí),可以采取拒絕或者引流等機(jī)制。
二、常用的限流算法
常用的限流算法有兩種: 漏桶 算法和 令牌桶 算法。
它的主要目的是控制數(shù)據(jù)注入到網(wǎng)絡(luò)的速率,平滑網(wǎng)絡(luò)上的突發(fā)流量。漏桶算法提供了一種機(jī)制,通過(guò)它,突發(fā)流量可以被整形以便為網(wǎng)絡(luò)提供一個(gè)穩(wěn)定的流量。 漏桶可以看作是一個(gè)帶有常量服務(wù)時(shí)間的單服務(wù)器隊(duì)列,如果漏桶(包緩存)溢出,那么數(shù)據(jù)包會(huì)被丟棄。 用說(shuō)人話的講:
漏桶算法思路很簡(jiǎn)單,水(數(shù)據(jù)或者請(qǐng)求)先進(jìn)入到漏桶里,漏桶以一定的速度出水,當(dāng)水流入速度過(guò)大會(huì)直接溢出,可以看出漏桶算法能強(qiáng)行限制數(shù)據(jù)的傳輸速率。

圖1 漏桶算法示意圖
對(duì)于很多應(yīng)用場(chǎng)景來(lái)說(shuō),除了要求能夠限制數(shù)據(jù)的平均傳輸速率外,還要求允許某種程度的突發(fā)傳輸。這時(shí)候漏桶算法可能就不合適了,令牌桶算法更為適合。如圖2所示,令牌桶算法的原理是系統(tǒng)會(huì)以一個(gè)恒定的速度往桶里放入令牌,而如果請(qǐng)求需要被處理,則需要先從桶里獲取一個(gè)令牌,當(dāng)桶里沒(méi)有令牌可取時(shí),則拒絕服務(wù)。

圖2 令牌桶算法示意圖
令牌桶算法的原理是系統(tǒng)會(huì)以一個(gè)恒定的速度往桶里放入令牌,而如果請(qǐng)求需要被處理,則需要先從桶里獲取一個(gè)令牌,當(dāng)桶里沒(méi)有令牌可取時(shí),則拒絕服務(wù)。 令牌桶的另外一個(gè)好處是可以方便的改變速度。 一旦需要提高速率,則按需提高放入桶中的令牌的速率。 一般會(huì)定時(shí)(比如100毫秒)往桶中增加一定數(shù)量的令牌, 有些變種算法則實(shí)時(shí)的計(jì)算應(yīng)該增加的令牌的數(shù)量, 比如華為的專(zhuān)利"采用令牌漏桶進(jìn)行報(bào)文限流的方法"(CN 1536815 A),提供了一種動(dòng)態(tài)計(jì)算可用令牌數(shù)的方法, 相比其它定時(shí)增加令牌的方法, 它只在收到一個(gè)報(bào)文后,計(jì)算該報(bào)文與前一報(bào)文到來(lái)的時(shí)間間隔內(nèi)向令牌漏桶內(nèi)注入的令牌數(shù), 并計(jì)算判斷桶內(nèi)的令牌數(shù)是否滿足傳送該報(bào)文的要求。
從最終用戶訪問(wèn)安全的角度看,設(shè)想有人想暴力碰撞網(wǎng)站的用戶密碼;或者有人攻擊某個(gè)很耗費(fèi)資源的接口;或者有人想從某個(gè)接口大量抓取數(shù)據(jù)。大部分人都知道應(yīng)該增加 Rate limiting,做請(qǐng)求頻率限制。從安全角度,這個(gè)可能也是大部分能想到,但不一定去做的薄弱環(huán)節(jié)。
從整個(gè)架構(gòu)的穩(wěn)定性角度看,一般 SOA 架構(gòu)的每個(gè)接口的有限資源的情況下,所能提供的單位時(shí)間服務(wù)能力是有限的。假如超過(guò)服務(wù)能力,一般會(huì)造成整個(gè)接口服務(wù)停頓,或者應(yīng)用 Crash,或者帶來(lái)連鎖反應(yīng),將延遲傳遞給服務(wù)調(diào)用方造成整個(gè)系統(tǒng)的服務(wù)能力喪失。有必要在服務(wù)能力超限的情況下 Fail Fast。
另外,根據(jù)排隊(duì)論,由于 API 接口服務(wù)具有延遲隨著請(qǐng)求量提升迅速提升的特點(diǎn),為了保證 SLA 的低延遲,需要控制單位時(shí)間的請(qǐng)求量。這也是 Little’s law 所說(shuō)的。
還有,公開(kāi) API 接口服務(wù),Rate limiting 應(yīng)該是一個(gè)必備的功能,否則公開(kāi)的接口不知道哪一天就會(huì)被服務(wù)調(diào)用方有意無(wú)意的打垮。
所以,提供資源能夠支撐的服務(wù),將過(guò)載請(qǐng)求快速拋棄對(duì)整個(gè)系統(tǒng)架構(gòu)的穩(wěn)定性非常重要。這就要求在應(yīng)用層實(shí)現(xiàn) Rate limiting 限制。
常見(jiàn)的 Rate limiting 的實(shí)現(xiàn)方式
Proxy 層的實(shí)現(xiàn),針對(duì)部分 URL 或者 API 接口進(jìn)行訪問(wèn)頻率限制
Nginx 模塊:
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=5;
}
參考: ngx_http_limit_req_module
基于 Redis 功能的實(shí)現(xiàn):
這個(gè)在 Redis 官方文檔有非常詳細(xì)的實(shí)現(xiàn)。一般適用于所有類(lèi)型的應(yīng)用,比如 PHP、Python 等等。Redis 的實(shí)現(xiàn)方式可以支持分布式服務(wù)的訪問(wèn)頻率的集中控制。Redis 的頻率限制實(shí)現(xiàn)方式還適用于在應(yīng)用中無(wú)法狀態(tài)保存狀態(tài)的場(chǎng)景。