【從一台 Server 到分散式架構】第 13 篇:10 萬人同時上線——限流、排隊與降級
上一篇,小明他們拆出了 User Service,掛上了 API Gateway,架構往微服務邁出了第一步。
還沒來得及慶祝,行銷部門就來找了他們。
「下週五下午三點,我們要辦一個全平台限時特賣,所有課程打五折,只有兩個小時。已經在 IG 和 YouTube 上大量宣傳了,預估會有十萬人同時湧入。」
小傑看了一眼目前的監控面板。平常尖峰大概有五千人同時在線,QPS 約兩千。十萬人的話,是平常的二十倍。
「我們現在的架構,能撐住嗎?」
沉默了幾秒,他們都知道答案:不能。
🏗️ 本篇開始前的架構 — API Gateway 在位,但沒有任何流量保護機制
雖然已經有了 API Gateway 做路由與認證,但面對突如其來的二十倍流量,這扇門後面的每一個服務都是裸奔的。
不限流,會發生什麼?
很多人以為「流量大只是後端比較忙」。實際上,超過承載上限的流量,後果比你想的嚴重得多:
-
資料庫連線池被占滿:後端服務通常維護一個資料庫連線池,例如最多同時開 100 條連線。十萬個請求同時進來,全都要等連線,連線池瞬間滿了,後面的請求全部 timeout。
-
記憶體耗盡,服務崩潰:每一個進來的請求都需要記憶體來處理。請求塞進來的速度遠超過處理速度,記憶體耗盡,服務直接 OOM(Out Of Memory)重啟。
-
雪崩效應(Cascade Failure):Order Service 慢了,Course Service 在等 Order Service 的回應,User Service 又在等 Course Service……一個服務慢,拖著下游全部一起慢,最後所有服務都在互相等待,整個系統進入死鎖狀態。
最糟糕的結局:不是「網站很慢」,而是所有人都看到 500 錯誤,下單失敗,課程看不了。
生活化的比喻:百貨公司週年慶
把這個情境想成實體百貨公司辦週年慶。
開門的瞬間,門口擠了三千個人。如果這三千人全部一起衝進去,會發生什麼?電梯癱瘓、手扶梯爆炸、現場擁擠、收銀機卡住、最糟的情況是發生踩踏。
有經驗的百貨公司會怎麼做?
- 限制入場速度:每次只開放幾十個人進場,其他人在門口排隊等。
- 設置排隊區:入口外面拉起柵欄,工作人員維持秩序,用號碼牌管理排隊。
- 暫時關閉次要區域:週年慶當天,美食街先不接受內用(降低服務範圍),把人力和空間全部留給主要的促銷區。
- 熱門專櫃先暫停:若某個專櫃人太多,貼告示「目前人數已滿,請稍後再來」,而不是讓大家全擠進去。
對應到系統:限流、排隊、降級,就是這些策略的數位版本。
第一道防線:限流(Rate Limiting)
限流的核心概念很簡單:規定在一個時間窗口內,最多只允許多少個請求通過。
超過上限的請求,直接在 API Gateway 擋掉,回傳 429 Too Many Requests,讓前端告訴用戶「請稍後再試」。
限流通常有兩個維度:
每用戶限流(Per-User Rate Limiting)
防止單一用戶瘋狂重試,例如:
- 每個用戶每秒最多送 10 個購買請求
- 每個 IP 每分鐘最多查詢 100 次課程資訊
這主要是防濫用和防機器人,即使一般流量正常,也應該要有。
全站限流(Global Rate Limiting)
這是用來保護後端服務的,例如:
- Order Service 最多同時處理 500 個 QPS,超過就暫時擋住
- 每分鐘全站最多接受 30,000 個請求,多的先擋在 Gateway
全站限流讓後端服務只接收它「真的能處理」的量,不被無差別地打垮。
常見的限流演算法
工程師常提到這幾種演算法,直覺上了解就好:
| 漏斗(Leaky Bucket) | 請求以固定速率「滴出」,超過流速的先排隊等 | 輸出很穩定,適合保護下游 |
小明他們的 API Gateway(如 Kong)通常內建這些算法,只需要設定參數就能開啟,不用自己實作。
🤔 限流是不是犧牲了可用性?
這是一個非常深刻的問題。如果你還記得第 10 篇談到的 CAP 理論,可用性(Availability) 的定義是:任何請求都應該收到一個(非錯誤的)回應。
當我們回傳 429 Too Many Requests 時,從技術定義上來說,我們確實犧牲了部分請求的可用性。
但這是一個「以小換大」的策略:
- 如果不限流:系統會因為過載而崩潰(例如資料庫卡死、服務重啟),導致 100% 的使用者都面臨全站不可用。
- 如果限流:我們犧牲了那 20% 超額流量的可用性,但換取了剩下的 80% 核心流量可以穩定、快速地被處理。
所以,限流其實是**「局部犧牲可用性,以保全系統的生存」。在分散式系統的世界裡,這叫作「優雅降級」**的第一步。
第二道防線:排隊(Virtual Queue)
限流擋住了一批請求,但用戶看到「429 請稍後再試」,有多少人會真的等?大多數人會瘋狂重試,形成更大的洪峰。
更好的體驗是:讓用戶進入一個虛擬等待室,告訴他「你排在第 8,342 號,預計等待 3 分鐘」,然後系統每秒放行固定數量的人進入購買流程。
這就是你在搶票網站、限量球鞋預購、演唱會搶票時常見到的排隊頁面,它的本質是:
- 使用者進來先收到一個排隊號碼(Token),存在 Redis 或 Queue 裡。
- 系統每秒從 Queue 裡取出固定數量的 Token,讓對應的使用者進入真正的購買流程。
- 其他人繼續停在等待頁面,每幾秒輪詢自己的排隊狀態。
對後端來說,壓力不再是瞬間的十萬個請求,而是每秒穩定的幾百個,這才是系統真的能消化的速度。
第三道防線:降級(Graceful Degradation)
就算做了限流和排隊,在流量暴衝的時刻,後端還是會比平常忙很多。此時的策略是:把有限的資源集中在最重要的功能上,主動關閉或簡化次要的功能。
小明的課程平台,哪些是核心功能,哪些是次要功能?
| 核心功能(不能關) | 次要功能(可以暫時關) |
|---|---|
| 使用者登入 / 認證 | 推薦課程演算法 |
| 課程購買 / 結帳 | 評論區 |
| 影片播放 | 課程全文搜尋 |
| 訂單查詢 | 學習進度分析報表 |
降級策略具體可以長這樣:
- 推薦演算法太慢:降級回「顯示熱門課程固定排行(從快取讀,不跑演算法)」。
- 搜尋服務壓力大:暫時隱藏搜尋欄位,或回傳「搜尋服務暫時不可用」。
- 評論區 DB 壓力大:暫時停止讀取評論,顯示「評論暫時維護中」。
降級不等於「系統壞了」,它是一個主動的、有計劃的取捨:犧牲舒適,保住核心。
第四道防線:熔斷(Circuit Breaker)
即使做了降級,一個下游服務還是可能出問題。這時候有一個非常重要的保護機制叫做熔斷器(Circuit Breaker)。
它的名字來自電路的保險絲。當電路過載時,保險絲斷開,防止整個線路燒壞;等問題解決,再重新接上。
在系統裡,熔斷器在服務與服務的呼叫之間工作:
三個狀態:
- 關閉(Closed):正常呼叫。熔斷器持續監控錯誤率,一切正常就讓請求通過。
- 開啟(Open):當下游服務的錯誤率在短時間內超過閾值(例如 5 秒內 50% 的請求失敗),熔斷器「跳開」。之後的請求不再嘗試呼叫下游,直接回傳一個預設的錯誤或降級結果。這樣下游服務才有時間恢復,而不是被持續的請求淹沒。
- 半開(Half-Open):冷卻一段時間後,熔斷器放行少量試探性請求去叫下游。如果成功,回到「關閉」;如果還是失敗,回到「開啟」繼續等。
熔斷器解決的是雪崩效應:一個服務壞了,不讓它拖垮整條鏈上的所有服務。
現實對照:雙 11 和演唱會搶票
這套組合拳(限流 + 排隊 + 降級 + 熔斷)在現實世界裡被大量使用:
阿里巴巴雙 11:每年雙 11 零點,阿里會先做一波「流量預熱」——提前把快取加熱、部分服務做靜態化(把商品頁 HTML 預先生成存在 CDN,不打 DB),搶購瞬間把所有寫入請求先進訊息佇列,讓後端按節奏消化,而不是真的同時跑十億筆 DB 寫入。
演唱會搶票(KKTIX / 拓元):你一進去就被放進虛擬等待室,拿到一個排隊號碼。系統每秒放行固定人數進入搶票頁面。你在等待室等時看到的進度條,其實就是 Queue 在消化。
這些都是同一套思維:系統不可能無限擴充,總有物理上限。在物理上限內,用管控進場速度來換取系統的穩定性,比讓所有人同時衝進去然後一起掛掉,體驗好太多了。
小明的行動計劃
距離限時特賣還有一週,小明和小傑列出了一份緊急清單:
- 在 API Gateway 設限流規則:每 IP 每秒最多 20 個請求;Order Service 的
/checkout路徑,全站 QPS 上限設為 500。 - 建立排隊等待頁面:使用 Redis 的 Sorted Set 實作虛擬 Queue,用戶進來先拿號碼,每秒放行 300 人進入結帳流程。
- 列出降級清單:限時特賣期間,自動關閉推薦演算法(改用快取固定排行)、暫停評論讀取、隱藏學習進度分析。
- 在服務間加熔斷器:Course Service 呼叫 User Service 時加熔斷,連續失敗超過 10 次就開啟熔斷,回傳「使用者資料暫時不可用」而不是讓請求卡死等超時。
- 提前做壓力測試:用壓測工具模擬五千、一萬、三萬的併發請求,找出哪個環節先到瓶頸。
🏗️ 本篇結束後的架構 — 有了限流、排隊、降級、熔斷的保護層
流量進來的第一關在 API Gateway,進不去的先排隊或擋住;進得去的服務也有熔斷器互相保護,次要功能主動降級讓出資源。
小結與預告
這篇我們學了面對流量暴衝的四道防線:
- 限流(Rate Limiting):規定每個時間窗口最多放行多少請求,API Gateway 擋在最前面。
- 排隊(Virtual Queue):讓用戶進等待室,系統按節奏放行,把瞬間洪峰拉平成穩定水流。
- 降級(Graceful Degradation):主動關閉次要功能,把資源集中給核心功能。
- 熔斷(Circuit Breaker):服務間互相保護,一個壞了不讓它拖垮整條鏈。
這四件事的核心邏輯只有一句話:系統總有上限,與其讓洪水把所有東西一起沖垮,不如主動控制水閘,讓最重要的東西先活著。
限時特賣順利過了,小明鬆了一口氣——直到下週的每日例會上,DBA(資料庫管理員)小陳打開了監控儀表板:「雖然前面的限流和排隊救了我們,但你看這張 DB 查詢延遲的圖……讀取的壓力在特賣結束後還是沒有下來,而且還在慢慢往上走。」
小明看了看圖,想起了第 6 篇做的讀寫分離——當時只加了一個 Read Replica。現在流量持續成長,一個 Replica 已經不夠了。
下一篇,我們來聊:當讀取的壓力繼續成長,一主一從已經不夠用,要怎麼做讀寫分離進階與多從庫擴展。