Skip to main content

Command Palette

Search for a command to run...

Lesson 26 : 系統韌性的守護者-限流、熔斷與背壓的設計模式

Published
2 min read

當這幾個名詞出現後,代表我們進到了一個高併發/大流量的系統了。在這個章節中,我們一起來看看如何透過一些方式來避免高併發導致我們的系統crash掉。

限流(Rate Limiting)

相信大家對這個名詞並不陌生,限流其實就是字面上的含意,限制流量。

限流的目的是保護「接收方」,確保系統不會因為瞬間的高併發請求而癱瘓。

限流通常發生在 API Gateway 或服務的最前端。它像是一個夜店門口的保全,控制每分鐘可以進場的人數,當超過限制時,通常回傳 HTTP 429 Too Many Requests

常見演算法

  1. 令牌桶 (Token Bucket): 系統以固定速率產生令牌,並存於有限容量的桶中,請求需取得令牌才能通過,允許一定程度的突發流量

  2. 漏桶 (Leaking Bucket): 無論請求多快,輸出速率永遠固定。主要用於平滑流量。

  3. 固定/滑動視窗: 計算一段時間內的請求總數,簡單但可能在視窗切換點出現兩倍流量。

演算法 核心邏輯 優點 缺點 適用場景
令牌桶 (Token Bucket) 桶子存令牌,有令牌就放行。 允許突發流量 (Burst),靈活性高。 實作稍微複雜一點點。 Google API、大多數微服務 Gateway。
漏桶 (Leaking Bucket) 請求進桶子,固定速率流出。 絕對平滑,強行保護後端穩定。 沒辦法處理突發需求,請求會排隊很久。 資料庫寫入保護、電信信令控制。
固定視窗 (Fixed Window) 每分鐘重算一次。 實作最簡單,記憶體佔用極低。 臨界點問題:例如 0:59 進 100 個,1:01 進 100 個,瞬間其實進了 200 個。 簡單的 API 配額限制。
滑動視窗 (Sliding Window) 視窗隨時間滾動。 解決了臨界點翻倍的問題。 儲存每個請求的時間點,較耗記憶體。 較精細的用戶級別限流。

關於平滑流量

如果一個系統每秒只能處理 10 個請求:

非平滑狀態: 第 1 秒突然衝進來 50 個請求,系統瞬間崩潰或發生延遲,而後面的 4 秒卻空空如也。

平滑後(漏桶算法): 這 50 個請求會先進入一個「桶子」排隊,系統以每秒 10 個的固定速度緩慢釋放。雖然請求處理完畢的總時間變長了,但後端系統始終保持在負載範圍內,不會被打掛。

補充

在Java Web 開發中,tomcat的預設值是200個connection,Web Server 透過執行緒池 (Thread Pool) 來實現流量控制。

然而在PHP-FPM 的流量控制邏輯是透過一個主進程管理多個子進程(Worker Processes)在排隊處理任務,並透過 pm.max_children 旯管理最大子進程數量。

實務上, PHP-FPM (pm.max_children) 與 Tomcat (maxThreads) 本質上是併發數限制 + 請求排隊機制,當 Worker 滿了,請求進入作業系統的 Backlog 佇列,如果連佇列都滿了,就會回傳錯誤。

PHP的限流

在 PHP 的世界裡,我們通常不會單靠 PHP-FPM 來擋流量,而是會配合:

  1. Nginx limit_req:在最前端就限制每秒請求數。

  2. Opcache:減少編譯時間,讓「漏水」的速度變快。

  3. Queue (Laravel Job):遇到大流量時,把耗時任務丟到後台處理,讓 PHP-FPM 趕快空出來接下一個請求。

熔斷 (Circuit Breaker)

保護「呼叫方」,防止故障蔓延。通常運用在Server to Server的系統中,防止服務雪崩。

情境

當 A 服務呼叫 B 服務,而 B 服務反應過慢或持續報錯時,A 服務應該「切斷」對 B 的請求,直接回傳預設的錯誤(Fallback),而不是讓執行緒(Thread)全部卡在那裡等待超時。

熔斷的三種狀態

狀態 表現行為 轉換條件
Closed 流量正常通過。 錯誤率低於門檻。
Open 請求直接攔截,執行 Fallback。 錯誤率或超時率超過設定值(如 50%)。
Half-Open 放行少量請求測試。 經過一段「冷卻時間」後自動進入。

實作補充

如果是Java的開發者,可以使用:Resilience4j

而Laravel的開發者,可以使用:laravel-circuit-breaker

背壓 (Backpressure)

協調「生產者」與「消費者」的速度差異。

在非同步系統(如 Queue)中,當生產者發送數據的速度快過消費者處理的速度時,如果沒有背壓,消費者的Buffer會溢位導致 Out of Memory。

背壓不僅存在於 Queue,也廣泛應用於網路層(TCP Flow Control)

常見策略

  1. 控制速率 (Signaling): 消費者主動告訴生產者:「我現在只能處理 10 個,別傳太快。」

  2. 緩衝 (Buffer): 先存在記憶體,但有上限。

  3. 丟棄 (Drop): 處理不完就直接丟掉最新的(或最舊的)。

總結

模式 誰保護誰 觸發時機 典型案例
限流 伺服器保護自己 請求量超過預設閾值 阻擋惡意爬蟲、API 配額限制
熔斷 呼叫方保護自己與系統 依賴的外部服務不穩定時 第三方支付 API 回應超時
背壓 消費者保護自己 處理速度跟不上輸入速度時 數據流處理 (Data Streaming)

限流(Rate Limiting):發生在入口(Ingress),保護系統 熔斷(Circuit Breaker):發生在服務間(Service-to-Service),防止雪崩 背壓(Backpressure):發生在非同步系統(Async Pipeline),避免資源耗盡

從Rookie到Junior,一個後端成長的30堂課

Part 1 of 31

“從 Rookie 到 Junior:一個後端成長的 30 堂課” 是一套專為後端新手所設計的成長型技術系列文章。內容以實務為導向,逐步拆解後端工程的核心能力,包括程式語言基礎、架構思維、框架運作原理、業務邏輯設計、資料庫操作、以及常見的開發模式。 本系列的目標是協助讀者從零散的學習堆疊,建立成體系的後端知識框架。讀者能夠理解各語言背後不變的工程思維與設計原則。這套內容旨在讓學習者從「會寫程式」進入「能理解系統設計」的階段,逐步具備勝任 Junior Backend Engineer 的能力。

Up next

Lesson 25: 淺談 單體架構、微服務架構與單/多租戶架構

過去我們討論了 要把程式寫在哪、程式要怎麼拆的題目,接下來我們來看看「如何服務不同客戶」,這些架構反映了軟體開發在擴充性與複雜度之間的權衡。 軟體架構深度解析:從系統拆分到商業規模化 在軟體工程的演進中,架構的選擇往往是在「開發效率」、「系統擴充性」與「營運成本」之間尋求平衡。我們可以從兩個核心維度來觀察這些架構:系統如何運行(單體 vs. 分散式) 以及 如何服務客戶(單租戶 vs. 多租戶)。

More from this blog

Lesson 25: 淺談 單體架構、微服務架構與單/多租戶架構

過去我們討論了 要把程式寫在哪、程式要怎麼拆的題目,接下來我們來看看「如何服務不同客戶」,這些架構反映了軟體開發在擴充性與複雜度之間的權衡。 軟體架構深度解析:從系統拆分到商業規模化 在軟體工程的演進中,架構的選擇往往是在「開發效率」、「系統擴充性」與「營運成本」之間尋求平衡。我們可以從兩個核心維度來觀察這些架構:系統如何運行(單體 vs. 分散式) 以及 如何服務客戶(單租戶 vs. 多租戶)。

Mar 26, 20262 min read

Lesson 24: 資料庫擴展術-讀寫分離、複寫機制與快取一致性挑戰

為什麼要讀寫分離? 大多數的 Web 應用都是 「讀多寫少」(例如:看文的人多,發文的人少,Heavy Read System)。當所有的請求都塞給同一台資料庫時,磁碟 I/O 和連線數會成為瓶頸。 Master (主庫): 負責寫入 (Insert/Update/Delete),確保數據一致性。 Slave (從庫): 負責讀取 (Select),可以有多個從庫來分擔讀取壓力。 為什麼讀

Mar 25, 20262 min read

面試經驗談 2025-2026

從2025年3月開始,我陸陸續續參與了從新創到上市櫃公司的Senior - Tech Lead的相關面試,其中有不乏 尊重面試者、展現高度專業的企業(公司),當然也有遇到幾場面試鬼故事,這篇文章主要分享我對於軟體工程師面試的方向分享,以及部分鬼故事,以此警惕自己不要成為這樣的面試官。 AI的洪流,改變了SWE的生態 LLM的發展確確實實的影響到了軟體工程師的生態。 過去受限於算力與資料規模,深度學

Mar 23, 20262 min read

Lesson 23: 系統的緩衝區-Queue 佇列與非同步處理 (Asynchronous)

佇列 佇列的實作工具非常多,舉凡AWS SQS、RabbitMQ、Kafka…等。 佇列的特性,其實是一個非常強大的系統緩衝區,應用層面非常廣。 什麼是佇列? 佇列可以想像成,在既有流程中外,有另一個”水管”,來連接原有的資料流(或邏輯過程),其中 呼叫方將資料 推(Push)到水管中,接受方(監聽) 從水管中將資料拉(Pull)出處理 為什麼佇列是「強大的緩衝區」? 在同步處理中,系統像是一

Mar 23, 20262 min read

Bennett's Tech Blog | 後端架構、系統設計

32 posts

來自台灣的軟體工程師,相信軟體可以改變世界