分散式系統中,級聯故障是最可怕的
網際網路服務提供商面臨快速增長挑戰的同時還要管理不斷增長的系統分佈。儘管服務的可靠執行對像 Google、Amazon 和 Co. 等這樣的大公司來說非常重要,但它們的系統還是會一次又一次地出現故障,導致大量中斷和帶來糟糕的客戶體驗。
舉幾個例子,比如深受影響的 Gmail(2012)[1]、AWS DynamoDB(2015)[2] 以及最近的 Facebook(2021)[3]。在這種情況下,人們經常會遇到所謂的級聯故障,導致超出普通系統故障的不良併發症。但是,考慮到他們的預算和技術知識,即使是線上業務的大玩家,為什麼也不能完全避免這種故障呢?你可以為自己的系統使用哪些切實可行的風險緩解方法?
這篇文章是想帶你瞭解如何透過防止故障傳播來提高大型分散式系統的彈性。
級聯故障是由於正反饋迴圈而隨時間增加的故障。典型行為最初由單個節點或子系統故障觸發。然後它會將負載分散到其他系統的節點上,這反過來又進一步增加系統故障的可能性,從而導致惡性迴圈或滾雪球效應。
級聯故障的重要性體現在三個方面:首先,它們可以在短時間內導致整個服務停機。其次,受影響的系統不會像處理常見的問題那樣恢復正常,而是會逐漸惡化。最終要依賴人為干預才行。最後,在最壞的情況下,級聯故障可能會在沒有警告的情況下突然發生,因為負載分佈和故障會迅速發生。
這篇文章主要關注點是分散式計算環境中的級聯故障,但它們也可能發生在其他各種領域,例如,電力傳輸、金融、生物學以及生態系統。因此,它們是一種相當普遍的現象,與自然界中發現的模式有些相似。為了更好地瞭解電腦科學中的級聯故障是什麼樣的,讓我們看一個具體的案例。
AWS DynamoDB 是一種高可擴充套件的非關聯式資料庫服務,分佈在多個資料中心,提供高度一致的讀取操作和 ACID 事務。它被 Netflix、Airbnb 和 IMDb 等多個知名的網際網路公司所使用。
我們要研究的級聯故障示例的事件發生在 2015 年 9 月 20 日,當時 DynamoDB 在美國東部地區超過四個小時不可用。涉及兩個子系統:儲存伺服器和後設資料服務,兩者都在多個資料中心有副本。儲存伺服器向後設資料服務請求其資料分割槽分配的所謂成員資格。如圖 1 所示。
圖 1:儲存伺服器和後設資料服務
對於成員資格(也用於資料分割槽的分配)的請求,存在相應的超時時間。如果超時了,則相應的儲存伺服器會重試並將其自身排除在服務之外。
該事件的一個不幸的先決條件是 DynamoDB 引入的一個新特性,稱為全球二級索引(GSI)。這使客戶可以更好地訪問他們的資料,但缺點是會顯著增加後設資料表的大小。因此,導致處理時間變得很長。同時不幸的是後設資料服務的容量和成員請求的超時時間沒有做出相應的調整。
真正的問題是由於一個短暫的網路問題導致一些儲存伺服器(處理非常大的後設資料表)成員資格請求超時,這些伺服器變得不可用並且還不斷的重試它們的請求。
這就導致後設資料服務超負荷運轉,進而減慢響應速度並導致更多伺服器重新提交其成員資格請求,因為它們也達到了超時時間。結果,後設資料服務的狀態進一步惡化。儘管多次嘗試增加資源,系統仍然陷入故障迴圈數小時。最終,問題只能透過中斷對後設資料服務的請求來解決,即服務基本上離線。
結果是美國東部地區發生了廣泛的 DynamoDB 中斷,這是一個典型的級聯故障例子。但是,陷入這種錯誤迴圈的系統的底層概念和模式是什麼?
首先,要說的是級聯故障的觸發點看起來是多種多樣的。例如,可能是新推出的特性、維護、流量流失、cron 作業、分散式拒絕服務(DDoS)、限流等。它們的共同點是它們在一組有限資源的上下文中工作,這意味著可能會出現伺服器過載、資源耗盡和服務不可用等影響 。讓我們詳細看看這些:
伺服器過載
最常見的原因是伺服器過載。發生這種情況時,系統效能下降通常會影響系統的其他區域。如圖 2 所示,在初始場景(左)中,來自兩個反向代理的負載分佈在叢集 A 和 B 之間,因此叢集 A 以每秒 1000 個請求的假設最大容量執行。
在第二種情況(右)中,叢集 B 發生故障,整個負載都到達叢集 A,這就會導致叢集A過載。叢集 A 現在必須每秒處理 1200 個請求並開始出現異常行為,導致效能遠遠低於所預期的每秒 1000 個請求。
資源耗盡
伺服器的資源是有限的。如果負載增加到某個閾值以上,伺服器的效能指標(例如,延遲或錯誤率)就會惡化,這意味著更高的崩潰風險。隨後的影響取決於導致瓶頸的資源型別,例如:
如果 CPU 不足,可能會出現各種問題,包括請求速度較慢、排隊效應過多或執行緒不足。
如果記憶體/RAM 被過度使用,任務可能會崩潰,或者快取命中率會降低。
此外,執行緒飢餓可能直接導致錯誤或導致健康檢查失敗。
在這種情況下對主要原因進行故障排除通常很痛苦。這是因為所涉及的元件是相互依賴的,並且根本原因可能隱藏在複雜的事件鏈之後。例如,假設可用於快取的記憶體較少,導致快取命中次數減少,因此後端負載較高,以及此類組合。
服務不可用
當資源耗盡導致伺服器崩潰時,流量會轉到其他伺服器,從而增加這些伺服器崩潰的可能性。這樣一個伺服器的崩潰迴圈就建立了。更壞的情況是這些問題會一直保持在系統中,因為某些機器仍然處於關閉狀態或正在重新啟動的過程中,而持續增加的流量會阻止它們完全恢復。
一般來說,當我們將流量從不健康節點重新分配到健康節點時,總是存在級聯故障的風險。這可能是編排系統、負載均衡器或任務排程系統的情況。為了解決級聯故障,我們需要仔細研究所涉及的元件之間的關係。
從 DynamoDB 的案例中可以看出,修復級聯故障非常棘手。尤其是從大型科技公司的角度來看,分散式系統增加了很多複雜性,這使得跟蹤各種互連變得更加困難。
我們這裡使用一種被稱為因果迴圈圖(CLD)的方法來描述這些(級聯)關係。CLD 是一種建模方法,有助於視覺化複雜系統中的反饋迴路。圖 3 視覺化了 AWS DynamoDB 中斷的 CLD。
解釋如下:箭頭表示初始變數和後續變數之間的動態。例如,如果後設資料服務的延遲增加,超時次數就會增加,所需的重試次數也會增加。如果系統中的影響是高度不平衡的,即正負的數量在很大程度上不相等,則存在一個加強迴圈。這意味著系統可能對級聯故障很敏感。
圖 3:2015 年 AWS DynamoDB 中斷的因果迴圈圖
現在,針對級聯故障場景,我們有好多種措施可以採用。第一個也是最直觀的選擇是增加資源。在上圖中,可以看到在迴圈中後設資料服務容量引入了減號。如果增加,它會減弱迴圈的增強,不過,這可能沒有用,正如我們在 AWS 中看到的那樣。除了增加資源外,還可以採用其他策略:
儘量避免健康檢查失敗,以防止系統因過度健康檢查而死亡。
如果出現執行緒阻塞請求或死鎖,請重新啟動伺服器。
顯著降低流量,然後慢慢增加負載,以便伺服器可以逐漸恢復。
透過丟棄某些型別的流量切換到降級模式。
消除批處理/不良流量,透過減少非關鍵或錯誤工作來減輕系統負載。
這個可能會讓系統的某些服務不可用並且客戶是能夠感知到的,因此最好首先避免級聯故障。
有許多方法可以使分散式系統對級聯故障具有魯棒性。
一方面,大型網際網路公司已經在思考如何防止系統陷入級聯錯誤,比如透過對錯誤進行隔離,為此市面上已經開發出來許多工具和框架。例如,Hystrix(來自 Netflix),一個延遲和容錯庫,或者 Sentinel。對於前者,Netflix 已經做出了進一步的發展,即自適應併發限制(可以在此處閱讀更多內容[4])。但總的來說,這些工具都是將外部呼叫包裝成某種資料結構,試圖抽象出關鍵點。
另一方面,就是目前正在發展的技術,有一些複雜的解決方案,例如,實現所謂的sidecar代理,諸如 Istio 這樣的服務網格。其他的一些示例比如 Envoy 或 Haproxy。
除了這些解決方案之外,我們還要牢記某些系統設計概念。例如,嘗試減少系統中同步呼叫的數量。透過應用釋出-訂閱模式設計(比如使用 Kafka)從編排(orchestration)模式轉變為協調(choreography)模式。面對不斷增加的流量,這種解決方案通常會更健壯。其他方法例如,執行容量規劃(取決於用例)也可能有所幫助。這通常意味著實施自動供應和部署、自動擴充套件和自動修復的解決方案。在這種情況下,對 SLA 和 SLO 的密切監控就顯得很重要。
現在,為了更好地理解底層解決方案的方法,我們可以看看分散式系統中的典型反模式,在級聯故障的情況下應該避免這些反模式。Laura Nolan 提出了其中的六項,我們會就風險緩解策略方面進行討論。
反模式 1:接受數量不受限制的請求
佇列/執行緒池中的任務數量應該是受限的。這可以在請求過多的情況下控制伺服器何時以及如何慢下來(slow down)。該設定應該在伺服器可以達到峰值負載的範圍內,但不要太多從而導致它阻塞。在這種情況下,對於系統和使用者來說,快速失敗總比長時間掛起要好。在代理或負載均衡器方面,通常是透過速率限制策略來實現,例如,用來避免 DDoS 和其他形式的伺服器過載。
但是還有其他許多方面要考慮的,例如,在佇列管理的上下文中,因為大多數伺服器線上程池前面都有一個佇列來處理請求。如果數量增加超過佇列的容量,請求將被拒絕。佇列中等待的大量請求會佔用更多記憶體並增加延遲。如果請求的數量接近恆定,那麼一個小佇列或不需要佇列就可以了。這意味著如果流量增加,請求會被立即拒絕。如果預期會有更大的偏差,則應使用更長的佇列。
此外,為了保護伺服器免受過度負載的影響,減載和優雅降級的概念是可行的選擇。負載脫落用於在過載的情況下儘可能地保持伺服器的效能。這是透過簡單地返回 HTTP 503(服務不可用)狀態碼來確定請求優先順序的方法丟棄流量來實現的。
一個更復雜的變體是優雅降級,它會逐漸切換到較低質量的查詢響應。這些可能會執行得更快或更有效。但是,這一定是一個經過深思熟慮的解決方案,因為它會給系統增加很多複雜性。
反模式 2:危險的(客戶端)重試行為
為了減少系統的工作量,確保避免過度的重試行為是很重要的。指數退避是一種合適的方法,它的做法是重試的時間間隔連續增加。還可以使用所謂的抖動(jitter)機制,即在重試間隔中新增隨機噪聲。這可以防止系統被累積的“負載波”擊中,這也稱為重試放大(參見圖 4)。
圖 4:重試放大的典型模式
此外,還有一種稱為熔斷器的設計模式。熔斷器可以被認為是一種開關。在初始狀態下,來自上游服務的命令被允許傳遞給下游服務。如果錯誤增加,熔斷器會切換到開啟狀態,系統會快速出現故障。這意味著上游服務出錯,允許下游服務恢復。一段時間後,請求再次逐漸增加。例如,在 Hystrix(上面已經提到)中,實現了某種熔斷器模式。
減輕危險重試行為的另一種方法是設定伺服器端重試預算,設定每分鐘可以重試請求的數量。超出預算的所有內容都將被丟棄。但是,我們要綜合全域性來看。一定要避免在軟體架構的多個級別上執行重試,因為這可能會呈指數級增長。
最後,需要注意的是,重試應該是冪等的並且沒有副作用。無狀態呼叫 在系統複雜性方面也是有益的。
反模式 3:因輸入錯誤而崩潰
系統應確保伺服器不會因輸入錯誤而崩潰。此類崩潰與重試行為相結合,可能導致災難性後果,例如,一臺伺服器接著一臺相繼崩潰。在這方面,尤其應仔細檢查來自外部的輸入。使用模糊測試是檢測這些型別問題的好方法。
反模式 4:基於鄰近的故障轉移
確保不要把所有流量都重定向到最近的資料中心,因為它也可能會過載。此處適用的邏輯與叢集中單個伺服器的故障相同,也就是一臺機器接著一臺發生故障。
因此,為了提高系統的彈性,必須在故障轉移期間以受控方式重定向負載,這意味著必須考慮每個資料中心的最大容量。基於 IP-Anycast 的 DNS 方式最終會將流量轉發到最近的資料中心,這可能會出現問題。
反模式 5:失敗引起的工作
故障通常給系統帶來額外的工作。特別是,故障發生在少數幾個節點上,最終可能會給剩餘其他節點帶來大量的額外工作(例如,副本)。這可能會帶來有害的反饋迴圈。一種常見的緩解策略是延遲或限制副本數量。
反模式 6:啟動時間長
一般而言,在開始時處理過程通常較慢。這是因為例項需要做初始化過程和執行時最佳化。故障轉移後,服務和系統經常由於負載過重而崩潰。為了防止這種情況,我們希望系統可以更快的啟動。
此外,快取在系統啟動時通常是空的。這使得查詢變得更加昂貴,因為它們必須去原始地方拿資料。因此,崩潰的風險高於系統在穩定模式下執行時的風險,因此請確保保持快取可用。
除了這六個反模式之外,還有其他系統元件或引數需要檢查。
例如,可以檢視請求或 RPC 呼叫的截止日期(deadline)。一般來說,很難設定好的截止日期。但是在級聯故障的情況下,經常遇到的一個常見問題是客戶端超過了許多設定的截止日期,這意味著資源的大量浪費。
AWS DynamoDB 示例從一開始也是這種情況。通常情況下伺服器應該檢查請求離截止日期是否還有時間剩餘,從而可以避免工作的浪費。一種常見的策略是所謂的期限傳播。也就是請求樹的頂部有一個絕對的截止日期。再往下的伺服器只得到前一個伺服器完成計算後剩下的時間值。例如,伺服器 A 的期限為 20 秒,計算需要 5 秒,那麼伺服器 B 的期限為 15 秒,依此類推。
級聯故障是分散式系統中一種即可怕又特殊的現象。這是因為有時必須採取違反直覺的路徑來避免它們,例如實際上旨在減少錯誤的定製化工作,比如看似智慧的負載平衡,可能會增加完全失敗的風險。
有時,最好的策略就是向客戶顯示一條錯誤訊息,而不是實施複雜的重試邏輯並冒著 DDoS 攻擊系統的風險。但是,有時候又不得不做出妥協。測試、容量規劃和在系統設計中應用某些模式有助於提高系統的彈性。
畢竟,大型科技公司的經驗教訓和事後分析為進一步採取行動以避免未來出現級聯故障提供了很好的指導。但是,最新技術和趨勢也值得關注。
相關連結:
https://www.sentinelone.com/blog/irreversible-failures-lessons-from-the-dynamodb-outage-2/ https://netflixtechblog.medium.com/performance-under-load-3e6fa9a60581
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2924554/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 分散式系統2:分散式系統中的時鐘分散式
- 什麼是分散式系統中的冪等性分散式
- GlusterFS分散式儲存系統中更換故障Brick的操作記錄分散式
- 什麼是分散式系統分散式
- 可能是講分散式系統最到位的一篇文章分散式
- 什麼是分散式系統!以及分散式系統架構的優缺點!分散式架構
- Alluxio在多級分散式快取系統中的應用UX分散式快取
- 分散式系統中ID的需求分散式
- 分散式系統1:什麼是分散式系統——簡要的介紹與定義分散式
- 分散式系統中的分散式鏈路追蹤與分散式呼叫鏈路分散式
- 分散式 - 分散式系統的特點分散式
- [譯] 分散式系統如何從故障中恢復?— 重試、超時和退避分散式
- 最簡單的分散式檔案系統 go-fastdfs分散式GoAST
- 輕量級分散式檔案系統fastDFS分散式AST
- 分散式系統中的領導選舉分散式
- 分散式系統中的事務問題分散式
- 分散式系統分散式
- 史上最淺顯易懂的Git分散式版本控制系統教程Git分散式
- 什麼是分散式系統的利特爾定律? - nurkiewicz分散式
- 什麼是對分散式系統有自己的理解分散式
- 分散式系統的跟蹤系統分散式
- 分散式系統中的自主自治計算 - pathelland分散式
- 分散式系統中的一些問題分散式
- 分散式系統中的CAP、ACID、BASE概念分散式
- 分散式系統中 Unique ID 的生成方法分散式
- 分散式系統:系統模型分散式模型
- 分散式系統(三)——分散式事務分散式
- Eventloop不可怕,可怕的是遇上PromiseOOPPromise
- 分散式:分散式系統下的唯一序列分散式
- 大家都在說的分散式系統到底是什麼?分散式
- 大型分散式網站架構:快取在分散式系統中的應用分散式網站架構快取
- 我理解的分散式系統分散式
- 分散式系統的問題分散式
- 理解分散式系統中的快取架構(下)分散式快取架構
- 理解分散式系統中的快取架構(上)分散式快取架構
- 分散式系統中唯一 ID 的生成方法分散式
- [分散式]分散式計算系統淺析分散式
- 你男朋友是高可用麼? | 談分散式系統的概念分散式