分散式系統為何這麼怪
AWS 用很直白的方式解釋分散式系統為何難搞:一次請求會跨過多個故障域,每一步都可能獨立失敗。

1999 年的 Amazon,伺服器少到會幫機器取名。像是「fishy」和「online-01」。聽起來很可愛,但問題一點也不萌。只要系統跨過多台機器,故障就不再是偶發 bug,而是日常。
在 AWS Builders’ Library 這篇文章裡,核心意思很直接。一次請求看起來像一個函式呼叫。實際上,它會變成一串網路傳送、驗證、狀態更新和回應。每一步都可能壞掉。
所以你會覺得分散式系統很怪。不是你太菜。是它本來就違反直覺。單機程式的思路,到了多機環境,常常直接失靈。
三種系統,三種痛法
訂閱 AI 趨勢週報
每週精選模型發布、工具應用與深度分析,直送信箱。不定期,不騷擾。
不會寄垃圾信,隨時可取消。
AWS 把分散式系統分成三類。離線系統、軟即時系統、硬即時系統。這個切法很實用。因為不同工作負載,能承受的失敗成本差很多。

離線系統最舒服。像批次運算、影像渲染、資料分析。慢一點沒關係。重試幾次也沒差。反正沒人坐在螢幕前等它回來。
軟即時系統介於中間。像搜尋索引更新、EC2 憑證傳播、壞機器偵測。它們要快,但不是每毫秒都要命。延遲可以忍,前提是結果最後要對。
硬即時系統就比較慘。像 Web API、訂單流程、付款授權、電話系統。使用者現在就在等。你不能慢吞吞,也不能一直重送。
- 離線系統:批次處理、資料分析、渲染農場
- 軟即時系統:搜尋索引、狀態同步、憑證更新
- 硬即時系統:API、付款、訂單、直播控制流程
- 痛點差異:可不可以重試,差很多
這個分類很重要。因為容錯策略會跟著變。離線系統可以慢慢補。硬即時系統卻得在幾百毫秒內做決定。這時候,網路抖一下,整個服務體驗就會歪掉。
講白了,分散式系統不是單機程式加幾台伺服器。它是另一種世界觀。你一旦跨過故障邊界,假設就全部要重寫。
一次呼叫,拆成八個步驟
文章最有感的地方,是它把 request/reply 拆開看。單機上的函式呼叫,看起來很單純。跨網路之後,它會變成八個步驟。送出請求、傳送、驗證、更新伺服器狀態、送出回應、傳回、驗證、更新客戶端狀態。
這八步不是在湊字數。每一步都能獨立失敗。請求可能根本沒送出去。伺服器可能收到了,但在回應前掛掉。回應可能到了客戶端,卻因為版本不合而被丟掉。
你可以把它想成線上遊戲的 Pac-Man。你在本機寫下 board.find("pacman"),看起來像查陣列。實際上,背後可能是遠端呼叫、序列化、timeout、重送、再驗證。
“The network enables sending messages from one fault domain to another.” — Amazon Builders’ Library
這句話很狠。也很準。網路把兩個故障域連在一起。然後你就不能再把它當成一個函式呼叫看待。你得把它當成一段有風險的協議。
我覺得很多團隊卡在這裡。程式碼看起來乾淨。測試也綠燈。上線後卻開始出現怪問題。原因通常不是邏輯錯,而是時間錯。分散式系統最愛用 timing 搞人。
故障不是例外,是常態
AWS 一直強調 failure。因為在分散式系統裡,failure 不是少數情況。它就是日常。單機世界裡,CPU 壞了或 kernel panic,整台機器通常一起死。這叫 fate sharing。雖然慘,但好理解。
跨機器就不一樣了。客戶端活著,伺服器死了。伺服器收到了請求,卻在回應前當機。網路丟包,但兩邊都顯示健康。這種情況最煩,因為你很難一眼看出誰出包。
所以工程師才會一直講 timeout、retry、idempotency、versioning。這些詞聽起來很無聊,但它們就是生存工具。少一個,API 就可能變成半套系統。
下面這些步驟,都可能壞:
- 請求送出失敗,因為網路斷線或連線被拒
- 請求送達後,伺服器中途當掉
- 請求驗證失敗,因為資料格式不合或版本不一致
- 伺服器更新狀態失敗,因為儲存或記憶體出錯
- 回應送出失敗,明明已經處理完了
- 回應傳回失敗,封包在路上消失
- 客戶端驗證失敗,因為收到不完整資料
- 客戶端更新狀態失敗,因為本地狀態已經變了
這就是為什麼分散式團隊很愛做重試保護。不是矯情。是因為你根本不知道失敗發生在哪一步。更麻煩的是,有些失敗你只看 log 還抓不到。
假設伺服器已經做完事,卻在回應前死掉。客戶端只看到 timeout,然後重送一次。這時候如果你的 API 不是 idempotent,就很容易做兩次同樣的事。付款、下單、扣庫存,全部會炸。
單機呼叫和網路呼叫,差很多
單機上的函式呼叫,失敗面很小。記憶體、CPU、執行緒,差不多就在同一個故障域裡。你可以把它想成一個房間裡的事情。出事了,通常是整間一起出事。
網路呼叫就像把事情交給另一棟樓。你多了傳輸、序列化、驗證、延遲、封包遺失。每一層都可能出問題。這也是為什麼分散式系統常常看起來像在跟現實打架。
下面這個比較很直觀:
- 單機呼叫:一個故障域,一個 process,一個記憶體空間
- 網路呼叫:至少兩個故障域,外加中間那條網路
- 單機失敗:通常整個 process 直接掛
- 網路失敗:一邊活著,另一邊卡住或死掉
- 離線工作:可以慢慢重試
- 硬即時工作:要在時間內回應,不能拖
這也解釋了,為什麼很多人第一次接觸微服務會翻白眼。看起來只是多拆幾個服務。實際上,失敗模式直接暴增。你不只是在寫軟體。你是在設計一套會跟時間賽跑的協議。
講得更白一點,單機世界的錯誤通常是「壞了」。分散式世界的錯誤常常是「半壞」。而半壞最難搞,因為它會騙你。
這跟雲端服務有什麼關係
如果你在做 API、支付、控制平面,這篇 AWS 文章其實是在提醒你一件事。跨機器之後,失敗就是正常輸入,不是例外。你不能等它發生才補救。你得先設計好。
這也是為什麼大型雲端服務很在意冗餘、重試、去重、版本相容性。每一個看起來很 boring 的功能,背後都在處理現實世界的爛狀況。說真的,這些東西比花俏的 UI 更重要。
你如果想把這件事再往下挖,可以看 AWS Builders’ Library 的其他文章。裡面很多內容都在講 timeout、負載、隔離、失敗處理。也可以對照 Martin Fowler 的 Microservices。微服務不是魔法。它只是把複雜度換個地方放。
另一個很實際的參考是 Kubernetes。它幫你管排程和健康檢查,但不會幫你消滅分散式問題。你的服務還是要自己處理 timeout、重送、與狀態一致性。
真正該記住的事
這篇文章最值錢的地方,不是它講了多少術語。是它把分散式系統的本質講清楚了。只要請求要跨機器,事情就會變得不單純。每一步都可能失敗。每一次成功,也可能只是暫時的成功。
所以我會給開發者一個很實際的建議。下次你設計服務時,先別畫架構圖。先寫下三件事:timeout 多久、重試幾次、重送會不會重複執行。這三題答不出來,後面八成會出事。
如果你在做台灣常見的電商、金流、物流 API,這件事更重要。因為這些系統每天都在吃高流量。一次 timeout 可能只是小事。十萬次 timeout 就是事故。
我自己的預測很簡單。接下來幾年,會越來越多團隊把重點放在 failure design,而不是只拼功能速度。你可以先問自己一句:當 request 到了 server,但 reply 沒回來時,你的系統到底會做什麼?