速率限制與限制和其他 API 流量管理
可以肯定地說,大多數(shù)人了解紅綠燈等基礎(chǔ)設(shè)施的工作原理。然而,我們大多低估了基礎(chǔ)設(shè)施的重要性。由于普通駕駛員缺乏車道紀(jì)律,沒有交通系統(tǒng)的道路將完全混亂,而且在許多城市,道路網(wǎng)絡(luò)甚至無法跟上人口增長(zhǎng)。
這個(gè)類比延伸到 API 設(shè)計(jì)。正如不守紀(jì)律的司機(jī)會(huì)在道路上造成嚴(yán)重破壞一樣,惡意用戶也可以威脅您的應(yīng)用程序。此外,隨著用戶群的增長(zhǎng),管理流量激增變得至關(guān)重要。與道路的紅綠燈一樣,處理此問題的一種有效方法是速率限制。
在本教程中,我們將探討速率限制與限制以及其他 API 流量管理技術(shù)。我們將介紹它們的工作原理、如何實(shí)施它們、何時(shí)使用每種策略,并提供一個(gè)比較表來幫助您確定哪種方法最適合您的需求。
什么是速率限制?
速率限制是一種控制 API 上傳入和傳出流量的技術(shù),它通過對(duì) API 用戶在給定時(shí)間范圍內(nèi)可以發(fā)出的請(qǐng)求數(shù)設(shè)置預(yù)定義限制。這樣,您可以防止單個(gè)用戶壟斷您的 API 基礎(chǔ)設(shè)施資源,同時(shí)防止拒絕服務(wù) (DoS) 和暴力攻擊等惡意攻擊。
在這個(gè)場(chǎng)景下,速率限制是通過速率限制器實(shí)現(xiàn)的,該速率限制器會(huì)不斷檢查每個(gè)用戶的請(qǐng)求,以查看其請(qǐng)求是否在其請(qǐng)求限制內(nèi)或超出:
如上圖所示,如果用戶在其限制范圍內(nèi),則會(huì)處理請(qǐng)求,并更新其新限制。但是,如果請(qǐng)求已超出其限制,則請(qǐng)求將被拒絕。此外,這些限制的上限和補(bǔ)充頻率取決于組織的首選項(xiàng),而這些首選項(xiàng)又受系統(tǒng)容量和業(yè)務(wù)要求的影響。
實(shí)施速率限制
在實(shí)踐中,可以使用各種算法實(shí)現(xiàn)速率限制,每種算法都有自己的方法來管理請(qǐng)求速率。一些流行的包括:
- Token Bucket ---- 此算法使用具有固定令牌數(shù)量的“桶”。每個(gè)請(qǐng)求都會(huì)從存儲(chǔ)桶中刪除一個(gè)令牌;如果存儲(chǔ)桶為空,則請(qǐng)求被拒絕。
- Leaky Bucket ---- 與令牌桶類似,但在此桶中,令牌(或請(qǐng)求)以恒定速率處理。如果傳入請(qǐng)求速率超過處理速率,則超出的請(qǐng)求將被丟棄或延遲。
- Fixed Window ---- 在固定時(shí)間窗口(例如,每分鐘或每小時(shí))內(nèi)對(duì)請(qǐng)求進(jìn)行計(jì)數(shù)和限制。如果請(qǐng)求數(shù)超過窗口內(nèi)的限制,則進(jìn)一步的請(qǐng)求將被拒絕,直到下一個(gè)窗口。
- Sliding Log (滑動(dòng)日志)---- 此函數(shù)使用時(shí)間戳記錄每個(gè)請(qǐng)求。為了確定是否允許新請(qǐng)求,該算法會(huì)檢查日志中允許的時(shí)間范圍內(nèi)的請(qǐng)求數(shù),并根據(jù)該計(jì)數(shù)做出決定。
例如,我們可以利用以下代碼中顯示的模式,通過令牌桶算法實(shí)現(xiàn)速率限制:
class TokenBucket {
constructor(rate, capacity) {
this.rate = rate;
this.capacity = capacity;
this.tokens = capacity;
this.lastRequestTime = Date.now();
}
addTokens() {
const now = Date.now();
const elapsed = (now - this.lastRequestTime) / 1000; // Convert to seconds
const addedTokens = elapsed * this.rate;
this.tokens = Math.min(this.capacity, this.tokens + addedTokens);
this.lastRequestTime = now;
}
allowRequest(tokensNeeded = 1) {
this.addTokens();
if (this.tokens >= tokensNeeded) {
this.tokens -= tokensNeeded;
return true;
} else {
return false;
}
}
}
在這個(gè)代碼示例中,我們定義了一個(gè) TokenBucket 類,該類設(shè)置 Token 生成速率和容量,并記錄最后一次請(qǐng)求時(shí)間;然后,我們創(chuàng)建一個(gè) addTokens() 方法,該方法根據(jù)自上次請(qǐng)求以來的時(shí)間計(jì)算要添加的令牌數(shù)量,并額外更新用戶的當(dāng)前令牌計(jì)數(shù)。最后,我們定義了一個(gè) allowRequest() 方法,用于檢查請(qǐng)求是否有足夠的令牌,扣除任何必要的令牌,并返回是否允許請(qǐng)求。
在我們的應(yīng)用程序中應(yīng)用此實(shí)現(xiàn)將如下所示:
const bucket = new TokenBucket(1, 10); // 1 token per second, max 10 tokens
function handleRequest(userRequest) {
if (bucket.allowRequest()) {
// Request allowed
userRequest();
} else {
console.log("Too many requests, please try again later");
}
}
function getPosts() {
fetch('/path/to/api')
}
handleRequest(getPosts);
在此使用示例中,我們初始化一個(gè)新的 TokenBucket 實(shí)例,速率為每秒 1 個(gè)令牌,容量為 10 個(gè)令牌。然后,我們創(chuàng)建一個(gè) handleRequest() 函數(shù)來檢查請(qǐng)求是否被允許并打印適當(dāng)?shù)南ⅰN覀冞€使用假設(shè)的 getPosts() 函數(shù)測(cè)試我們的請(qǐng)求處理程序。
此示例雖然是用 JavaScript 編寫的,但應(yīng)該能夠幫助您開始以任何語言實(shí)現(xiàn)速率限制或使用令牌存儲(chǔ)桶算法。
幾乎所有的語言和框架都有庫(kù),您可以使用這些庫(kù)輕松實(shí)現(xiàn)速率限制,而無需重新發(fā)明輪子;JavaScript 生態(tài)系統(tǒng)中的一些常用軟件包包括用于 Express.js 的 express-rate-limit 包和用于 NestJS 應(yīng)用程序的 @nestjs/throttler。
速率限制替代方案
API 流量管理不僅限于限流;還有其他方法可以控制應(yīng)用程序的使用情況和管理流量激增。讓我們?cè)谙旅婵焖偬剿魉鼈儭?/p>
Throttling 節(jié)流
限制是另一種控制用戶向 API 發(fā)出請(qǐng)求的速率的技術(shù)。與速率限制不同,速率限制會(huì)在超出限制時(shí)阻止請(qǐng)求,而限制會(huì)引入延遲來減慢請(qǐng)求速率:
憑借這種設(shè)計(jì)性質(zhì),限制可以消除流量峰值,而用戶只會(huì)遇到更少的拒絕和延遲請(qǐng)求。但是,一個(gè)缺點(diǎn)是,這些故意的延遲也會(huì)增加延遲并使系統(tǒng)變慢,因?yàn)槊總€(gè)請(qǐng)求都在隊(duì)列中等待處理。此外,與簡(jiǎn)單的速率限制相比,實(shí)現(xiàn)限制邏輯可能更復(fù)雜,在極端情況下,僅靠限制可能無法保護(hù)系統(tǒng)免受過載。
如何實(shí)施限制
可以通過保留請(qǐng)求時(shí)間戳隊(duì)列、計(jì)算給定時(shí)間段內(nèi)的請(qǐng)求數(shù)以及在請(qǐng)求速率超過允許的限制時(shí)引入延遲來實(shí)現(xiàn)限制。示例如下所示:
class Throttler {
constructor(maxRequests, period) {
this.maxRequests = maxRequests;
this.period = period;
this.requestTimes = [];
}
addTokens() {
// Filter out old request timestamps
}
allowRequest() {
// Check if current requests are below maxRequests
// If yes, log the current timestamp and allow the request
// If no, deny the request
}
delayRequest() {
// Calculate delay needed until the next request can be allowed
}
}
在此偽代碼示例中,Throttler 類通過保留請(qǐng)求時(shí)間戳隊(duì)列來管理請(qǐng)求速率。然后,addTokens() 方法會(huì)刪除早于設(shè)置時(shí)間段的請(qǐng)求時(shí)間戳。此外,allowRequest() 方法確定該時(shí)間段內(nèi)的請(qǐng)求量是否小于允許的最大值;如果是這樣,它會(huì)記錄當(dāng)前時(shí)間戳并允許請(qǐng)求。否則,它將拒絕請(qǐng)求。最后,delayRequest 方法估計(jì)允許下一個(gè)請(qǐng)求之前的時(shí)間。您還可以在此處查看此示例的完整 JavaScript 實(shí)現(xiàn)。
Spike control 峰值控制
峰值控制也叫削峰,是另一種流行的技術(shù),用于管理可能使 API 或服務(wù)不堪重負(fù)的突然激增的流量。它的工作原理是監(jiān)控短時(shí)間間隔內(nèi)的請(qǐng)求速率,并實(shí)施臨時(shí)阻止請(qǐng)求、重定向流量或擴(kuò)展資源等措施以適應(yīng)增加的負(fù)載。
例如,假設(shè) API 通常每分鐘可以處理 100 個(gè)請(qǐng)求。使用峰值控制,您可以設(shè)置一個(gè)閾值來檢測(cè)請(qǐng)求數(shù)是否突然躍升至每分鐘 150 個(gè):
如上所述,當(dāng)檢測(cè)到此類峰值時(shí),您可以將系統(tǒng)配置為通過暫時(shí)阻止新請(qǐng)求來防止過載,將流量重定向到其他服務(wù)器以平衡負(fù)載,或快速擴(kuò)展資源以管理增加的需求。
Circuit breaking 熔斷
熔斷也是管理 API 或服務(wù)彈性的有效技術(shù),尤其是在面臨故障或性能下降時(shí)。它的工作原理是監(jiān)控服務(wù)交互的運(yùn)行狀況并暫時(shí)停止對(duì)失敗服務(wù)的請(qǐng)求,以防止級(jí)聯(lián)故障。這里使用“service”這個(gè)詞也應(yīng)該暗示,與我們之前介紹的技術(shù)不同,熔斷在微服務(wù)架構(gòu)中比在整體式或簡(jiǎn)單的 API 系統(tǒng)中更受歡迎和有用。
想象一下您的服務(wù)與第三方 API 交互的場(chǎng)景。如果第三方 API 開始失敗或響應(yīng)緩慢,您的系統(tǒng)可以使用斷路器來檢測(cè)此問題,并在設(shè)定的時(shí)間內(nèi)停止向失敗的服務(wù)發(fā)出進(jìn)一步的請(qǐng)求。
當(dāng)斷路器檢測(cè)到多個(gè)連續(xù)故障或超時(shí)時(shí),它會(huì) “跳閘” 電路,暫時(shí)阻止對(duì)有問題的服務(wù)的新請(qǐng)求。在此期間,系統(tǒng)可以向用戶返回回退響應(yīng)或錯(cuò)誤消息。然后,在指定的超時(shí)時(shí)間后,斷路器允許有限的測(cè)試請(qǐng)求檢查服務(wù)是否已恢復(fù)。如果服務(wù)響應(yīng)成功,則電路將關(guān)閉,并恢復(fù)正常操作。如果故障繼續(xù),電路將保持打開狀態(tài),并再次阻止請(qǐng)求。
決定使用哪種 API 流量管理技術(shù)
決定使用哪種技術(shù)主要取決于您的應(yīng)用領(lǐng)域和要求。但是,考慮到我們到目前為止介紹的所有內(nèi)容,速率限制更適合需要強(qiáng)制實(shí)施嚴(yán)格請(qǐng)求配額的應(yīng)用程序,例如公有 API 或具有分層訪問級(jí)別的 API。限制更適合于維護(hù)性能和用戶體驗(yàn)至關(guān)重要的應(yīng)用程序,例如電子商務(wù)網(wǎng)站或社交媒體平臺(tái),因?yàn)樗鼤?huì)引入延遲而不是直接阻止請(qǐng)求,從而消除流量峰值:
對(duì)于遇到不可預(yù)測(cè)的流量激增的應(yīng)用程序,峰值控制將至關(guān)重要,例如,在高需求活動(dòng)期間向網(wǎng)站提供票務(wù),或在突發(fā)新聞期間向新聞網(wǎng)站提供票務(wù)。熔斷對(duì)于依賴多個(gè)外部服務(wù)(如微服務(wù)架構(gòu)或 SaaS 平臺(tái))的應(yīng)用程序特別有用,因?yàn)樗ㄟ^停止對(duì)失敗服務(wù)的請(qǐng)求來防止級(jí)聯(lián)故障,同時(shí)允許系統(tǒng)保持響應(yīng)。
還可以組合多種策略以實(shí)現(xiàn)更有效的流量管理。在某些情況下,您可以進(jìn)一步應(yīng)用負(fù)載均衡以在服務(wù)器之間分配流量。
比較速率限制、限制、尖峰控制和熔斷
下表突出顯示了我們介紹的不同 API 流量管理技術(shù)之間的主要差異,以幫助您快速確定哪種技術(shù)最適合您。
結(jié)論
在本教程中,我們探討了速率限制和其他 API 流量管理技術(shù),例如限制、峰值控制和熔斷。我們介紹了它們的工作原理、基本實(shí)現(xiàn)以及理想的應(yīng)用領(lǐng)域。采取某些流量管理措施來確保您的 API 能夠按預(yù)期為用戶提供服務(wù)非常重要,本文提供了一個(gè)快速指南,可快速幫助您決定如何以及何時(shí)使用哪種技術(shù)。
原文地址:
https://blog.logrocket.com/advanced-guide-rate-limiting-api-traffic-management/。
原文作者:Elijah Asaolu