面試官:如何設計一個百萬 QPS 的限流器?
今天秀才就帶大家來看另一個后端面試戰場上的經典系統設計問題:設計實現一個限流器。“設計限流器”其實也跟“設計短鏈系統”一樣,需要從多個維度出發,層層遞進,抽絲剝繭。看似簡單的功能背后,潛藏著分布式環境下的狀態同步、高并發處理的性能挑戰、不同業務場景的算法選型以及服務高可用等一系列復雜的工程難題。
下面秀才就帶大家庖丁解牛,從一個核心需求出發,逐步構建出一個穩健、高效、可擴展的百萬級QPS限流系統。讓你在面試中游刃有余,盡顯高手風范。

一、什么是限流器
拿到問題之后,還是老規矩,首先得搞明白面試官讓我們設計的究竟是一個什么東西。限流器顧名思義,是一種控制流量的機制,用于在高并發系統中限制用戶或服務的請求速率,避免系統因為過多請求而崩潰。比如允許用戶每分鐘發起 100 次請求,超出限額的請求將被拒絕響應
二、需求梳理
面試官:“我們來聊聊系統設計吧。如果要你來實現一個高性能的限流器,你會怎么設計?”
系統設計是典型的開放性問題,我們要做的第一步永遠是跟面試官對齊需求。
1. 功能需求
假設在本次面試中,面試官是讓你為某社交媒體平臺的 API 設計一個限流器。這就意味著我們限流器的功能是限制單個 HTTP 請求(如發布推文、獲取時間線或上傳照片),而不是更高層次的操作或業務功能。所以,我們要將重點討論用于控制流量和保護系統的服務端實現方案。雖然客戶端限流作為補充方案也有其價值,但這里不是我們討論的重點。
核心需求:
- 系統應通過用戶 ID、IP 地址或 API 密鑰識別客戶端,以應用適當的限制。
- 系統應根據可配置的規則限制 HTTP 請求(例如,每分鐘每個用戶 100 次 API 請求)。
- 當超出限制時,系統應拒絕請求并返回 HTTP 429 狀態碼,并包含有用的頭部信息(如剩余速率限制、重置時間)。
2. 非功能需求
明確系統功能之后,此時你應當向面試官確認規模預期。我們設計的這個系統,是為日請求量幾千次的初創公司API設計的,還是為一個需要每秒處理數百萬請求的大型平臺設計的呢?系統的規模將直接決定我們的架構選型。
假設面試官給出的目標是:每日1億活躍用戶,產生每秒100萬次請求(1M QPS)。
(1) 核心需求
這個量級,一下就將設計挑戰拉滿了。基于此,我們可以推導出具體的非功能性需求:
- 低延遲:限流邏輯本身不能成為性能瓶頸,其引入的延遲開銷應極小(例如,每次檢查耗時 < 10ms)。
- 高可用:限流系統作為核心基礎設施,必須具備極高的可用性。在分布式環境下,可以接受最終一致性,因為節點間限流狀態的微小延遲是可以容忍的。
- 高并發:系統必須能夠穩定處理每日1億活躍用戶帶來的每秒100萬次請求。
最后我們可以將需求整理出來,如果有白板的話,我們可以在白板上做一個需求呈現

三、底層設計
需求明確之后,我們可以開始勾勒系統的內部結構了,先從核心實體和接口定義開始。
“好的,根據剛才的需求,我們可以抽象出幾個核心實體來構建我們的限流模型。”
1. 核心實體定義
盡管限流器聽起來像個簡單的基礎設施,但其內部邏輯需要我們精確建模:
- 規則(Rule):定定義不同場景限制的速率限制策略。每條規則指定參數,如每個時間窗口的請求數、適用的客戶端以及覆蓋的端點。例如:“已認證用戶每小時可請求 1000 次”或“搜索 API 每分鐘每個 IP 允許 10 次請求。”
- 客戶端(Client):被限流的對象實體。它可以是一個用戶(通過UserID識別)、一個IP地址,或者一個API密鑰。每個客戶端都有一個關聯的限流狀態,用于實時追蹤其當前是否符合規則。
- 請求(Request):進入系統的、需要被評估的API請求。每個請求都攜帶了上下文信息,如客戶端身份、訪問的端點和時間戳,這些信息是判斷適用哪條規則的依據。
- 這三者協同工作:當一個請求到達時,系統首先識別出其對應的客戶端,然后查找適用于該客戶端的規則,最后檢查其當前使用量是否超限,從而決定是放行還是拒絕。
2. 系統接口設計
限流器本質上是一個被其他業務服務調用的基礎設施組件,其接口設計應當力求簡潔直觀。我們可以設計一個核心接口,供其他服務調用,以判斷請求是否應該被允許。
/**
* 檢查請求是否被允許
* @param clientId 客戶端唯一標識 (用戶ID, IP地址, 或 API Key)
* @param ruleId 規則唯一標識
* @return 一個包含是否通過、剩余次數和重置時間的對象
*/
isRequestAllowed(clientId, ruleId) -> {
passes: boolean, // 是否允許通過
remaining: number, // 剩余請求次數
resetTime: timestamp // 計數器重置的時間戳
}這個方法接收客戶端ID和規則ID,返回一個布爾值表示是否允許請求。同時,它還會返回客戶端需要知道的元數據,用于填充X-RateLimit-Remaining和X-RateLimit-Reset這兩個HTTP響應頭。
四、高層架構設計
我們首先得設計出一個滿足核心功能需求的最小可行產品(MVP)。它不需要具備擴展性或完美無缺,只是為后續開發奠定基礎。然后我們逐一檢查每個功能需求,確保高層設計能滿足所有要求。
1. 限流器應該部署在什么位置?
面試官:“實體和接口定義清楚了。那么從架構層面看,你認為這個限流器應該部署在系統的哪個位置?”
這是一個關鍵的架構決策點,它直接決定了限流器能獲取哪些上下文信息,以及它如何與系統的其他部分集成。你可以展示幾種方案,并分析其優劣,最終給出你的選擇。
“關于部署位置,業界主要有三種方案,各有優劣。我們可以逐一分析。”
(1) 進程內限流
最直接的想法是,讓每個應用服務或微服務自己內部實現限流邏輯。當請求進入服務時,服務檢查自己內存中的計數器,更新并決定是否放行。這種方式非常快,因為一切都在內存中完成,沒有網絡開銷。”

但這個方案有個致命缺陷:狀態不共享。每個服務實例只知道自己接收到的流量,無法看到全局的流量情況。假設我們限制一個用戶每分鐘100個請求,如果有5個服務實例,請求被均勻分發,那么每個實例可能只看到20個請求,都會誤認為‘遠未達到閾值’,從而輕松放行。但從全局看,該用戶實際上已經發起了100個請求。如果負載均衡策略變化,或者流量分布不均,限流效果就完全不可控了。所以,這種方案只適用于單體應用,或者能容忍限流精度嚴重偏差的場景。”
(2) 專用服務限流
第二種方案是限流器作為獨立微服務部署在客戶端與應用服務器之間。當應用服務收到請求后,先向這個限流服務發起一次RPC或HTTP調用,詢問:‘用戶A的這次請求是否允許?’限流服務維護著一個集中的計數器,返回‘允許’或‘拒絕’。”

這種架構非常靈活。應用服務可以傳遞豐富的業務上下文給限流服務,比如用戶的會員等級、賬戶狀態等,從而實現復雜的動態限流邏輯,比如“周五期間允許額外請求”這類復雜業務邏輯。還可以為系統不同部分配置獨立的限流服務,各自針對特定需求進行調優。最重要的是,由于狀態是集中管理的,限流的精度非常高。
不過,它的缺點也很明顯。首先是延遲,每次業務請求都憑空增加了一次網絡往返。即使限流服務響應很快(比如10ms),在高并發下,累積的延遲也是非常可觀的。其次,我們引入了一個新的單點故障風險。如果限流服務掛了,我們必須做出選擇:是‘故障開放’(fail-open),放行所有流量,可能壓垮后端;還是‘故障關閉’(fail-closed),拒絕所有請求,導致整個API不可用?這兩種選擇都很難接受。最后,運維復雜度也增加了。”
(3) API網關/負載均衡器層
“綜合考慮,最優的方案是將限流邏輯前置,集成在系統的入口層,比如API網關或負載均衡器上。所有外部請求在到達任何應用服務之前,必須先經過這一層。網關根據請求信息(如IP、請求頭、API密鑰)應用限流規則,直接決定是轉發給后端,還是立即返回HTTP 429。”
這種方法是生產系統中最主流的做法,因為它概念簡單且提供了強大的保護。應用服務器永遠不會看到被阻止的請求,因此它們可以完全專注于處理合法流量。

當然,它也有局限性,主要在于上下文信息有限。網關層通常只能獲取到HTTP請求本身的信息,很難感知到深層次的業務邏輯。例如,要實現‘高級會員享受10倍限額’這樣的規則,就需要會員狀態信息能通過JWT令牌等方式傳遞到網關層。
另一個關鍵問題是限流狀態的存儲位置。網關需要快速訪問計數器和時間戳,這通常意味著要使用 Redis 這類內存存儲。但這樣會引入外部依賴,還需要處理 Redis 響應緩慢或不可用的情況。
經過上面三種方案的分析,雖然各有其局限性,但是綜合來看。我們還是要選擇更優的第三種方案,在API網關/負載均衡器層實現限流。你可以這樣回復面試官:
“面試官,綜合以上分析,我選擇方案三,在API網關層實現限流。 這是業界最成熟的模式,既能實現集中控制,又避免了為每個請求增加額外的網絡調用開銷。”
2. 如何識別客戶端?
在確定了限流器在架構中的位置之后,現在我們需要專注下一個問題:如何識別客戶端?這兩個決策相互關聯——部署位置的選擇會影響你能輕松獲取哪些客戶端信息,而識別策略又會影響限流器的合理部署位置。
由于我們選擇了 API 網關的方法,我們的限流器只能訪問 HTTP 請求本身中的信息。這包括請求的 URL/路徑、所有 HTTP 頭(如 Authorization、User-Agent、X-API-Key 等)、查詢參數以及客戶端的 IP 地址。雖然我們理論上可以對外部數據庫或其他服務進行調用,但這會增加我們希望避免的延遲,因此我們將堅持使用請求本身的信息。
我們首先需要確定什么使“客戶端”具有唯一性。我們使用的鍵決定了如何應用限制。我們有三個主要選項:
- 用戶 ID:適用于經過身份驗證的 API。每個登錄用戶都有自己的速率限制配額。這通常以 JWT 令牌的形式出現在 Authorization 頭中。
- IP 地址:適用于公共 API 或沒有用戶賬戶的情況。但需注意 NAT 后或企業防火墻內的用戶。IP 地址通常出現在 X-Forwarded-For 請求頭中。
- API 密鑰:開發者 API 常用方式。每個密鑰持有者擁有獨立限額。最常見的是通過 X-API-Key 請求頭進行標識。
到了這一步,面試官很可能會提問:所有用戶都經過身份驗證嗎?這是一個需要 API 密鑰的開發者 API 嗎?等等。
在實際應用中,你可能需要結合多種限制。也許已認證用戶比匿名 IP 獲得更高的限制,而高級用戶可能獲得更多。這反映了真實系統不僅執行全局限制,還疊加多層規則。例如:
- 每個用戶的限制:“小明每小時可以發出 1000 次請求”
- 單 IP 限制:"該 IP 每分鐘可發起 100 次請求"
- 全局限制:"我們的 API 每秒總共可處理 50,000 次請求"
- 端點特定限制:“搜索 API 限制為每分鐘 10 次請求,但個人資料更新為每分鐘 100 次”
你的限流器需要檢查所有適用的規則,并執行最嚴格的那個。如果小明已經使用了他 1000 次請求中的 50 次,但他的 IP 地址已經達到了 100 次請求的限制,他就會被阻止。
3. 限流算法選定
面試官:“OK,部署在API網關是個合理的選擇。現在我們來聊聊最核心的部分:你打算用什么算法來決定一個請求是否應該被放行?”
這是設計的靈魂所在。你需要展示你對不同算法的理解,并能根據場景做出權衡。
“限流算法主要有四種,它們在精度、資源消耗和實現復雜度上各有千秋。我們可以逐一分析。”
(1) 固定窗口計數器
最簡單的方法是將時間劃分為固定窗口(如 1 分鐘的時間段),并在每個窗口中計數請求。對于每個用戶,我們會維護一個在每個新窗口開始時重置為零的計數器。如果在某個窗口內計數器超過限制,則拒絕新的請求,直到窗口重置。
例如,在每分鐘 100 次請求的限制下,時間窗口可能劃分為 12:00:00-12:00:59、12:01:00-12:01:59 等。用戶在每個窗口期內可以發起 100 次請求,之后必須等待下一個窗口開啟。

實現起來非常簡單。只需使用一個哈希表,將客戶端 ID 映射到(計數器,窗口起始時間)這對值即可。
{
"alice:12:00:00": 100,
"alice:12:01:00": 5,
"bob:12:00:00": 20,
"charlie:12:00:00": 0,
"dave:12:00:00": 54,
"eve:12:00:00": 0,
"frank:12:00:00": 12,
}這種算法實現簡單,但缺點在于窗口邊界的臨界問題。比如:限流閥值為每秒5個請求,單位時間窗口為1秒。如果在前0.5秒到1秒的時間內并發5個請求,接著在1秒到1.5秒的時間內又并發5個請求。雖然這兩個時間段各自都沒有超過限流閾值,但如果計算0.5秒到1.5秒的總請求數,則總共是10個請求,已經遠遠超過了1秒內不超過5個請求的限流標準。

(2) 滑動窗口計數
滑動窗口正是為了解決固定窗口的臨界問題而生的。它將一個大的時間窗口(比如1分鐘)分割成更小粒度的子窗口(比如60個1秒的子窗口)。每次請求到來,當前子窗口的計數器加一。當整個大窗口向右滑動時,它會丟棄掉最左邊的子窗口的計數值,并納入一個新的子窗口。整個大窗口的請求總數,就是所有子窗口計數器的總和。通過這種更平滑的窗口移動方式,滑動窗口避免了固定窗口在邊界上的突刺問題,使得限流控制更加精確。
雖然滑動窗口可以一定程度上解決窗口臨界流量已出問題,但是因為滑動窗口本質其實是將窗口粒度更小,但是 不管多小,仍然是以窗口來限制,所以總會存在流量不均導致的限流不準確問題
假設窗口以0.5s為小周期移動,如下圖,在【0.5s,1.5s】,【1.5s,2.5s】間其實都是合理的,不會有流量超出,但是其實在【0.8s,1.8s】間就有10個請求了,并沒有達到限流效果

(3) 漏斗桶
漏斗限流算法的核心思想是將請求存儲在一個漏斗中,漏斗以固定的速率漏出請求。如果漏斗被填滿,新到達的請求將被丟棄。請求可以以以不定的速率流入漏桶,而漏桶以固定的速率流出,所以漏斗算法可以將突發流量均勻地配,確保系統在穩定的負載下運行

但是這種方式同樣存在以下兩個問題:
- 無法動態調整流量:漏斗的漏出速率是固定的,不夠靈活,無法根據實際流量情況進行動態調整。
- 突發流量處理有限:在突發流量過大的情況下,漏斗可能很快被填滿,大量請求將被拒絕,可能會導致服務質量下降。
(4) 令牌桶(推薦)
想象每個客戶端都有一個桶,可以容納一定數量的令牌(突發容量)。令牌以穩定的速率(補充速率)被添加到桶中。每個請求會消耗一個令牌。如果沒有可用的令牌,請求就會被拒絕。
例如,一個桶可能容納 100 個令牌(允許最多 100 個請求的突發),并以每分鐘 10 個令牌的速度補充(穩定速率為每分鐘 10 個請求)。客戶端可以立即發出 100 個請求,然后必須等待令牌補充。
該算法既能處理持續負載(通過補充速率控制),又能應對臨時突發流量(通過桶容量控制)。實現也很簡單,只需為每個客戶端記錄(令牌數,最后補充時間)。難點在于選擇合適的桶容量和補充速率,以及處理"冷啟動"場景——閑置客戶端重新激活時會擁有滿桶令牌。

所以這里,選用令牌桶算法是最合適的。它在簡潔性、內存效率和處理真實流量模式之間取得了最佳平衡,既能適應 API 流量的突發特性,又能嚴格執行整體速率限制。
這里面試官又可能會問了,“令牌桶確實是不錯的選擇。但我們有百萬QPS,這么多用戶的令牌桶狀態,應該存放在哪里?如果存在各個網關實例的內存里,不又回到進程內限流的老問題了嗎?”
此時你就可以從容的想面試官介紹接下來更深入的設計了:
“您提到了關鍵。這個狀態必須是集中存儲和共享的。對于這種對性能要求極高、且數據結構簡單的場景,Redis 是不二之選。它是一個高性能的內存數據庫,所有API網關實例都可以訪問它,作為我們令牌桶狀態的唯一事實來源。”
“具體實現上,我們可以為每個用戶在Redis中維護一個Hash結構,存儲兩個關鍵信息:tokens(當前令牌數)和last_refill_timestamp(上次補充令牌的時間戳)。”
“當請求到達網關時,流程如下:
- 網關根據用戶ID,用HMGET從Redis獲取該用戶的令牌桶狀態。
- 在網關內存中,根據當前時間與last_refill_timestamp的時間差,以及令牌生成速率,計算出這段時間應該補充多少新令牌,更新本地的令牌數(但不能超過桶容量)。
- 判斷更新后的令牌數是否大于等于1。如果是,則請求允許通過,并將令牌數減1;如果否,則拒絕請求。
- 將更新后的令牌數和當前時間戳,用HSET寫回Redis。”
厲害的面試官很容易發現這里還有問題,“等一下,你這個‘讀-改-寫’的流程,在分布式環境下不會有并發問題嗎?比如兩個請求同時到達不同的網關實例?
確實存在競態條件(Race Condition)。兩個網關實例可能同時讀取到還剩1個令牌,都認為可以放行,結果就是多放行了一個請求。雖然Redis的MULTI/EXEC事務可以保證寫的原子性,但讀操作在事務之外,無法解決這個問題。
這里我們可以引入Lua腳本,最終的解決方案是,將整個‘讀-計算-更新’的邏輯封裝成一個Lua腳本,發送給Redis執行。Redis保證Lua腳本的執行是原子的。這樣,整個限流決策過程就在Redis服務端一氣呵成,徹底避免了競態條件。
MULTI
HSET 小明:bucket tokens <new_token_count>
HSET 小明:bucket last_refill <current_timestamp>
EXPIRE 小明:bucket 3600
EXEC之后還可以在這里可以總結一下選擇Redis的原因:
- 高性能:基于內存,簡單操作可達亞毫秒級響應。
- 原子操作:通過Lua腳本保證了并發安全。
- 高可用:支持主從復制和哨兵/集群模式。
- 自動過期:可以利用EXPIRE命令,自動清理長時間不活躍用戶的令牌桶數據,防止內存泄漏。”
到這里,一個基本可用的限流器就已經設計完了,最終結果是在所有網關實例中實現精確、一致的速率限制。無論小明的第 100 個請求發送到網關 A、B 還是 C,它們都能看到相同的令牌桶狀態并執行相同的限制。
五、擴展性設計:邁向百萬QPS
面試官:“方案很完善。但我們最初的目標是百萬QPS。單個Redis實例,即使性能再好,恐怕也扛不住這么大的流量吧?”
單個Redis實例的處理能力通常在10萬QPS級別,無法滿足100萬QPS的目標。因此,我們必須對Redis進行水平擴展,也就是部署一個Redis集群。
1. 如何擴展Redis?
我們需要將海量的用戶令牌桶數據,分散到多個Redis實例上。關鍵在于分片(Sharding)。
我們可以采用一致性哈希算法。當一個請求到來時,網關提取客戶端標識(UserID、IP或API Key),這里,對于認證用戶,我們對其用戶 ID 進行哈希以確定存儲其速率限制數據的 Redis 分片。對于匿名用戶,我們對其 IP 地址進行哈希處理。對于 API 密鑰請求,我們則對 API 密鑰進行哈希。
通過一致性哈希算法計算出該標識應該路由到哪個Redis分片。這樣可以保證同一個客戶端的所有請求,始終命中同一個Redis實例,確保其限流狀態的完整性。”

在生產環境中,我們通常會直接使用Redis Cluster方案。它內置了數據分片邏輯(通過16384個哈希槽),API網關只需要連接到集群,客戶端庫會自動處理請求到正確節點的路由,無需我們自己實現復雜的一致性哈希邏輯。假設我們部署10個Redis分片,每個分片承載10萬QPS,理論上就能達到100萬QPS的目標。
2. 如何保證高可用?
現在我們有了多個Redis分片,每個分片都成了系統的關鍵依賴。如果任何一個分片宕機,所有速率限制數據存儲在該分片上的用戶將失去速率限制功能。這會導致可用性問題,如果用戶在無法獲得正確的速率限制響應時開始積極重試,還可能引發級聯故障。我們需要對故障模式做出根本性決策:關閉故障還是開放故障?
面試官:“那當一個Redis分片不可用時,你選擇故障開放還是故障關閉?”
這是一個經典的取舍問題。
- 故障關閉(Fail-Closed):當無法連接Redis時,拒絕所有未知狀態的請求。這能最大限度地保護后端系統,但會犧牲可用性,可能在限流組件故障時導致API整體不可用。
- 故障開放(Fail-Open):當無法連接Redis時,放行所有請求。這能保證API的可用性,但犧牲了保護作用,可能在流量洪峰時因失去限流而導致整個后端系統雪崩。
對于我們這個社交媒體平臺,限流失敗往往和流量激增同時發生(比如熱點事件)。在這種情況下,保護后端系統是第一要務。因此,我會選擇故障關閉。短暫地拒絕一部分請求,遠比整個平臺崩潰的代價要小。”
當然,選擇故障模式只是兜底策略。更重要的是通過高可用設計來避免故障。標準的做法是為每個Redis分片配置主從復制(Master-Slave Replication)。每個主節點都有一個或多個從節點實時同步數據。當主節點故障時,可以自動或手動進行故障轉移(Failover),將一個從節點提升為新的主節點。Redis Sentinel或Redis Cluster本身就提供了強大的高可用和故障轉移能力。”

3. 如何最小化延遲?
每次限速檢查都需要與 Redis 進行一次網絡往返,這會增加用戶請求的延遲。盡管 Redis 操作通常在亞毫秒級別,但網絡開銷每請求可能增加幾毫秒。在每秒 100 萬次請求的情況下,這種延遲可能會成為一個問題。
這里我們主要有兩種優化手段:
- 連接池:API網關與Redis之間維護一個持久的連接池,避免為每個請求都重新建立TCP連接,從而省去握手帶來的開銷。
- 地理就近部署:將API網關和Redis集群部署在同一個數據中心或可用區內,確保極低的網絡延遲。對于全球化的服務,可以在全球多個Region部署多套限流設施,通過DNS等方式將用戶路由到最近的接入點。
當然還有一種優化手段,那就是本地緩存的方式,但是這里可能會存在一個本地緩存和redis緩存的一致性問題。當出現不一致的時候,陳舊的緩存數據可能導致不正確的速率限制決策。通常來說,前兩項優化基本上可以滿足一般的業務需求了
4. 如何處理熱點問題?
面試官:“如果某個用戶(比如一個爬蟲或一個熱門應用的后端服務)請求量特別大,把分配給它的那個Redis分片打滿了,怎么辦?”
這是一個典型的熱點Key問題。單個客戶端的請求壓垮一個分片,這通常意味著濫用行為或設計不當的合法客戶端。
- 針對濫用流量:我們可以設計一個‘自動拉黑’機制。當一個客戶端在短時間內持續觸發限流(例如,一分鐘內觸發10次),就自動將其IP或API Key加入一個臨時黑名單(也可以存在Redis里),在更上游的防火墻或網關層面直接拒絕其后續所有請求。
- 針對合法高流量客戶:可以提供更高等級的API套餐,為他們分配更高的限流閾值,甚至在物理上將他們路由到專用的、資源更豐富的限流集群。同時,也應該在API文檔和SDK中,倡導客戶端也實現合理的客戶端限流和退避策略,從源頭平滑請求。
5. 動態調整規則配置
面試官:“設計得非常全面了。最后一個問題,如果運營同學想臨時調整某個API的限流規則,比如在大促期間提高下單接口的限制,我們總不能每次都去修改配置發版吧?”
這里就涉及到限流規則的動態配置了,這其實是一個錦上添花的設計,如果在面試的時候你能夠考慮到,將是一個很大的加分項。
你可以這樣回答,“當然不能。限流規則必須支持動態配置。我們可以將規則存儲在一個外部的配置中心,比如Nacos、Apollo,或者簡單的數據庫表中。API網關實例會訂閱這些配置的變更。
訂閱的方式主要有以下兩種:
- 拉(Pull)模式:網關定時去配置中心拉取最新的規則,緩存在本地內存。這種方式的優點是實現簡單,配置服務可以簡單到只是一個數據庫表。但配置更新有延遲,在更改規則和該規則在所有網關中生效之間總存在一個時間窗口。如果由于攻擊需要緊急降低限制,您可能需要等待長達 30 秒才能使更改傳播。對于大多數操作場景,這種延遲是可以接受的,但在緊急情況下可能會出現問題。
- 推(Push)-based:網關與配置中心建立長連接。一旦配置發生變更,配置中心會主動將新規則推送給所有網關實例。這種方式實時性最好,但架構更復雜。需要額外處理連接故障、確保所有網關都能接收更新,并應對部分網關更新成功而其他網關失敗的場景。當推送系統不可用時,還需要設計回退機制。

對于大多數場景,基于輪詢的‘拉模式’(比如每30秒拉一次)已經足夠。如果對實時性要求極高,可以選擇基于推送的方案。”
六、小結
到這里,一個百萬QPS限流系統的設計差不多就完成了。
回顧我們的整個設計過程,這個方案有什么特點?首先是需求驅動的設計思路,我們沒有一上來就談技術,而是先搞清楚要解決什么問題。其次是技術選型的權衡邏輯,每一個關鍵決策點都有充分的對比分析,比如為什么選API網關部署、為什么用令牌桶算法、為什么用Redis集群。最后是對非功能性需求的關注,不僅要功能正確,還要考慮性能、可用性、可運維性等工程實踐問題。
面試官看重的是什么?不是你背了多少技術名詞,而是你能不能把一個模糊的問題變成清晰的解決方案。當你展示出這種結構化思考和全局權衡的能力時,就證明了你具備處理復雜系統問題的潛質。


































