Trouble with Distributed Systems – Unreliable Networks

之前的文章我們大多都是在談系統 出錯 了怎麼辦,諸如 節點掛掉怎麼做做副本 (replication) 時 Lag 怎麼辦 等等等等;一切就只是希望讓工程師們意識到,邊界條件(鬼故事)在現實世界中是會發生的,先了解,才能更好的處理它們。

分散式系統會因為各種原因出錯,所以從這章開始,是時候要把鬼故事升級成魔王故事了,我們將談談更多可能會發生的錯誤,用最大力氣假設系統若會故障就真的會故障,就像投資時要全盤考量各種風險那樣(但有些人沒在管)。

接下來我們會有最經典的網路問題、時鐘精度問題,最後就是在分散式系統中最有趣的,有關節點狀態的真與假,但首先先談談部份故障為何吧!

故障和部份故障 (Faults and Partial Failures)

當你寫一段程式在單一台電腦上,它的結果很好預測,成功執行或失敗;一個執行在獨立電腦的好軟體沒有道理會起肖(除非你看太多奇怪東西中毒了),當你的硬體正常運作時,相同的操作會得到相同的結果,但當你硬體發生問題時(例如記憶體損壞、某條排線鬆脫等等),其結果就是整個作業系統故障(例如 windows 的藍色當機畫面),一個好軟體在獨立電腦上執行的結果,要嘛正常,要嘛故障,沒有中間的。

當你的軟體開始執行在多台電腦上時,用網路連接,這情況就不一樣了,我們並不是在一個理想化系統模型中運行;在分散式系統中,總是有很多意想不到的方式出錯(例如有人不小心把某一機櫃的網路線拔掉了),稱為 部份故障 (partial failure),部份故障是不確定的。

雲端運算和超級運算 (Cloud Computing and Supercomputing)

這裡有幾個關於如何建立大規模運算系統的哲學:

  • 一邊是有著高效能計算 (High-performance computing) 能力的超級電腦們,它們有上千顆 CPU 用在需要密集科學資料運算的任務上,如天氣預測或分子動力計算。
  • 而另一邊是雲到極致的雲端計算,透過網路串連。
  • 傳統企業用資料中心落在上面 2 個極端之間。

這些哲學處理故障的方式很不一樣,超級電腦在任務中會時不時地建立狀態檢查點,當某台節點故障,通常的解法就是停止整個叢集,修復節點,然後從檢查點繼續計算,就像獨立電腦當機的意思一樣。

而雲端運算的分散式系統就必須要考量部份故障的可能性,軟體必須要有容錯機制,我們需要從不可靠的元件中建立可靠的系統,儘管你的系統只跑在幾個節點中也是一樣。

首先先來看一下最經典的網路問題吧!

不可靠的網路 (Unreliable Networks)

Replication – Leaders and Followers 之後的文章,我們的分散式系統都是都是聚焦在 無共享架構 (shared-nothing systems),節點間不共享記憶體或儲存,網路是這些機器之間唯一的溝通方式。

網路和資料中心的內部網路(通常是乙太網路)都是 非同步封包網路 (asynchronous packet networks),在此類型網路架構中,一個節點送出訊息(封包)給另一個節點後,是不保證它能完整抵達的,如下圖描述了一些挺好笑的情況。

寄件者甚至無法判斷封包是不是有送達,只有當你接收到回覆訊息才曉得,你永遠也不知道為什麼沒有收到回覆;所以一個最常使用的解決方法就是使用 timeout ,設定一段時間過後放棄等待。

實務上的網路故障 (Network Faults in Practice)

沒有人能免於網路問題的,像是 [鯊魚咬海底電纜造成損壞](nobody is immune from network problems)、交換器軟體升級時可能會觸發網路拓撲重設定、網路介面有時會丟棄所有 進入封包 (inbound packets) 但是會成功送出 外出封包 (outbound packets) 等等。

只要是透過網路來溝通的,它就是有可能會失敗。

檢測故障 (Detecing Fauls)

有些系統會自動檢測故障節點,像是:

  • 一台負載均衡器 (load balancer) 需要停止送 request 給故障節點。
  • 使用 single-leader 副本策略的分散式資料庫,如果 leader 故障,follower 節點之一就會接手成為新的 leader (Leaders and Followers – Handling Node Outages)。

你不能只依靠網路協定來檢查節點活著與否,不能只 ping 一下就認為應用系統活著,最好是透過應用系統回覆正向 Ok 的訊息來表達它自身的狀態;另外雖然 TCP 協定有自己 retry 的機制,保險起見在有應用系統端也是需要做 retry。

Timeout 和無上限延遲 (delay)

講到 timeout 就得聊聊 timeout 到底得設多久才好?

長時間的 timeout 就代表會等很久才宣告節點死亡,短時間的 timeout 則有可能會誤判節點死亡(它說不定只是暫時變慢一點了而已,或者網路故障),然後讓其他節點接手,導致同樣的 request 執行 2 次以及增加接手節點負擔(極端的可能就是造成連鎖故障全節點死亡)。

如果你的系統能保證最大的封包延遲時間為何的話,每個封包交付的時間為 d,再來假設正常的節點處理 request 的時間不超過 r ,如此我們就可以合理設計 timeout 時長為 2d+r

但不幸的是,非同步網路的延遲是無上限的,就像你開車上路旅遊一樣,到熱門風景區的路程總是會塞車,網路上的排隊就像是:

  • 如果數個不同的節點同時送出封包到同一目的地,網路交換機必須將它們排進 queue 裡,然後一一送入目的網路,如下圖 8-2,等待的過程就是人們有感的 網路壅塞 (network congestion),如果有太多資料導致網路交換器的 quque 滿了,後來的封包會被丟棄。
  • 當封包抵達目的地節點,如果所有 CPU 核心正在忙碌,該 request 也會被作業系統排進 queue 裡,直到應用系統能準備處理它。
  • 如果是虛擬環境,執行中的作業系統也會因為其他機器用 CPU 的關係而暫停幾毫秒時間,在此段時間裡,VM 是無法處理資料的,持續來的進資料也會被 VM monitor 排 quque(做緩衝)。

結論是:沒有所謂的 “正確” 的 timeout 時長,選擇 timeout 是一個很權衡 (trade-off) 的事情,你能實驗性的設定不同 timeout,然後測量整個網路延遲的變異程度,你也能憑感覺設定,最重要的是要有機制能自動調整 timeout,身為資料工程師要隨時假設網路壅塞、排隊 (queueing) 和無上限延遲都有可能會發生。

tshine73
tshine73
文章: 50

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *