一文告訴你怎樣構建健壯的分布式系統(tǒng)
我之前在這個博客上寫過什么是分布式系統(tǒng),以及它們如何以不得不處理更復雜的系統(tǒng)設計為代價為我們提供巨大的可擴展性。讓我們討論一下如何使分布式系統(tǒng)對隨機故障具有彈性,隨著系統(tǒng)變大,這種隨機故障會變得越來越普遍。
系統(tǒng)理論告訴我們,系統(tǒng)中相互關聯(lián)的部分越多,發(fā)生大故障的可能性就越大。因此,要構建一個彈性系統(tǒng),我們需要減少連接數(shù)量。如果無法做到這一點,我們需要實施“臨時”切斷與故障部分的連接的方法,以便錯誤不會級聯(lián)到其他部分。
每個組件都必須假設每個其他組件都會在某個時候發(fā)生故障,并決定當這些故障發(fā)生時它會做什么。
最后,我們需要在系統(tǒng)中建立一些緩沖區(qū)——一些放松的方法,如果不消除對它的要求,以便有松弛來處理意外情況。
1 最小化組件間依賴
分布式系統(tǒng)的組件相互通信以獲取數(shù)據(jù)或功能。在這兩種情況下,我們都可以通過將數(shù)據(jù)/功能推送到調用組件而不是遠程訪問來減少連接需求。
構建大規(guī)模分布式系統(tǒng)迫使我們放棄標準軟件工程的許多“最佳實踐”。要記住的關鍵是,當我們采用分布式系統(tǒng)的復雜性來實現(xiàn)可擴展性時,我們還需要盡可能地控制“分布”。
1.1 重復數(shù)據(jù)
如果我們經常從另一個組件訪問一些數(shù)據(jù),我們可以在我們的組件中復制它,而不必在運行時檢索它。這可以大大減少運行時依賴并幫助改善我們組件的延遲。
經常訪問但有一定規(guī)律性變化的數(shù)據(jù)可以通過定期緩存刷新來臨時緩存。更改頻率更低或從不更改的數(shù)據(jù)(例如客戶姓名)可以直接存儲在我們的組件中。如果/當這些數(shù)據(jù)發(fā)生變化時,我們可能需要做一些額外的工作,但是這種增加的小開銷通常是值得的,因為它可以提高彈性。
1.2 非規(guī)范化數(shù)據(jù)
非規(guī)范化是在組件內發(fā)生的一種特殊形式的重復。如果我們使用關系數(shù)據(jù)存儲,我們可以通過在主實體中復制數(shù)據(jù)來降低查看多個實體的成本。本地化分散數(shù)據(jù)以獲得更好性能的原則也適用于此。
1.3 庫
為了減輕另一個組件的功能依賴性,我們可以將遠程組件打包為庫并將其嵌入到我們的組件中。這并不總是可能的(它可能是用其他語言編寫的,或者太大而不能成為一個庫)并且會帶來一系列問題(功能的變化需要跨多個組件進行庫升級),但是如果功能很關鍵并且經常被大規(guī)模訪問,這是打破組件間連接并使其成為本地的可行方法。
2 隔離錯誤
錯誤隔離很重要,原因有兩個。一是個別錯誤在分布式系統(tǒng)中更常見(許多移動部件的簡單功能)。另一個是,如果我們不能防止整個系統(tǒng)中的聯(lián)鎖錯誤,那么我們首先就失去了構建復雜體的理由。
錯誤隔離的主要結構是 SLA。每個組件都聲明了一些質量參數(shù),它將在執(zhí)行功能時得到尊重。這些參數(shù)可以包括延遲、錯誤率、并發(fā)性等。
在此 SLA 之外,調用它的組件會假定它已失敗并需要自行采取適當?shù)拇胧H绻M件本身檢測到它無法維護其 SLA,它可以先發(fā)制人地告訴其調用者暫停并稍后再來調用。
為了保持整體系統(tǒng)健康,最好是快速失敗而不是在違反 SLA 的情況下成功。兩個組件(一個被喚起的和一個喚起的)都必須為此設置機制。
2.1 保護調用者
超時:如果被調用的組件在其 SLA 內沒有響應,調用者必須超時(放棄)并改用一些回退機制(即使它拋出錯誤)來維護自己的 SLA 并防止一連串的 SLA 違規(guī)。
重試:由于網(wǎng)絡不可靠,分布式系統(tǒng)中的許多錯誤只是隨機的。如果調用者自己的 SLA 允許,調用者可以重試該操作。重試的前提是操作的冪等性。即它不應該改變狀態(tài)或只做一次,即使它被調用了兩次。
斷路器:如果對組件的調用連續(xù)失敗,調用者可以通過“打開電路”切斷連接并停止調用一段時間。由于調用者已經有一些錯誤場景的備份行為,這節(jié)省了調用者寶貴的資源,這些資源本來會被浪費掉。停止調用還可以減少被調用組件的負載,并給它一些恢復的喘息空間。
斷路器庫具有定期輪詢有問題的組件并在其性能似乎已恢復正常時重新啟動調用流程的機制。
2.2 保護被調用
隨機間隔:雖然重試可以減少錯誤,但在一個頻繁使用的組件中出現(xiàn)一個小的性能問題可能會導致其所有調用者一次重試。這種“重試風暴”會造成負載峰值并阻止該組件恢復。為了防止這種情況,重試應該在它們之間有一個隨機的時間間隔,以便交錯加載。
背壓:如果一個組件檢測到自己承受過多的負載并且即將違反其 SLA,它可以搶先開始丟棄新請求,直到其性能得到控制。這比接受它知道它不能在 SLA 內提供服務或沒有完全崩潰風險的請求要好得多。
3 在系統(tǒng)中建立緩沖區(qū)
3.1 異步通信
消息總線之類的異步通信通道允許調用遠程組件,而無需非常嚴格的 SLA 依賴。通過讓被調用組件準備好而不是立即使用消息,系統(tǒng)對增加的工作負載的需求變得更加靈活。
3.2 彈性配置
可擴展性最終歸結為充分利用可用硬件。但是,如果看到規(guī)模增長,讓系統(tǒng)緩口氣的一個簡單方法是分配更多硬件。雖然這僅在我們能夠承受的成本范圍內是可行的,但它為我們提供了抵御不可預測的負載變化的最后一道防線。