分布式系統(tǒng)中的工程可靠性和容錯(cuò)性
用戶希望可以依賴提供給他們的服務(wù)。在實(shí)踐中,因?yàn)閭€(gè)別不可避免地的因素,可能會(huì)導(dǎo)致服務(wù)失敗,但即使如此,我們也要盡量避免服務(wù)失敗。
在本文中,我們將詳細(xì)討論什么是工程可靠性和容錯(cuò)性,并解釋Ably平臺(tái)是如何設(shè)計(jì)的,已達(dá)到工程可靠性和容錯(cuò)性。
作為討論的前提,首先是一些定義:
可靠性
用戶對(duì)產(chǎn)品或服務(wù)的可信賴程度。這意味著系統(tǒng)不僅可用,而且還設(shè)計(jì)了大量冗余措施,以繼續(xù)按照用戶的期望工作。
可用性
產(chǎn)品或服務(wù)在需要時(shí)的可用程度。這通常歸結(jié)為,在系統(tǒng)出現(xiàn)故障時(shí),是否能夠提供足夠的資源冗余。
什么是容錯(cuò)性?
容錯(cuò)性是指系統(tǒng)的某些組件或子系統(tǒng)出現(xiàn)故障時(shí),依然具備可用性和可靠性。
具備容錯(cuò)性的系統(tǒng)可以容忍故障,它們旨在減輕不利因素對(duì)系統(tǒng)的影響,并確保系統(tǒng)對(duì)用戶保持始終可用。容錯(cuò)技術(shù)可用于提高可用性和可靠性。
可用性可以粗略地認(rèn)為是保證系統(tǒng)的正常運(yùn)行;可靠性可以被認(rèn)為是系統(tǒng)在運(yùn)行期間提供服務(wù)的質(zhì)量--也就是說(shuō),確保在系統(tǒng)故障中盡可能有效地維護(hù)功能和用戶體驗(yàn)。
如果該服務(wù)無(wú)法提供使用,那就是可用性不足。如果服務(wù)可用,但在使用時(shí)偏離了用戶預(yù)期,那就是可靠性不足。容錯(cuò)設(shè)計(jì)方法解決了這些不足,為業(yè)務(wù)和用戶體驗(yàn)提供了連續(xù)性。
可用性、可靠性和狀態(tài)
在大多數(shù)情況下,容錯(cuò)設(shè)計(jì)的主要基礎(chǔ)是:冗余。提供超過(guò)服務(wù)所需的最小組件數(shù)量或容量。關(guān)鍵問(wèn)題是如何管理“冗余”。
在真實(shí)世界中,可用性和可靠性存在著區(qū)別:
- 可用性可以接受服務(wù)的短暫停止,好比更換汽車輪胎;
- 可靠性需要確保服務(wù)的連續(xù)性,那么“冗余”必不可少。比如飛機(jī)的發(fā)動(dòng)機(jī)不止一個(gè)。
- 連續(xù)性的要求會(huì)影響冗余容量的提供方式。
- 在分布式系統(tǒng)中,我們可以將組件分為兩類,分別為"有狀態(tài)"和“無(wú)狀態(tài)”。
無(wú)狀態(tài)組件在不依賴于任何狀態(tài)的情況下就能實(shí)現(xiàn)功能。每次服務(wù)調(diào)用都可以獨(dú)立完成,不依賴于上一次服務(wù)調(diào)用。這些組件的容錯(cuò)設(shè)計(jì)相對(duì)簡(jiǎn)單:只需提供足夠的資源,即使某些資源出現(xiàn)故障,也可以由其他無(wú)狀態(tài)組件負(fù)責(zé)處理。
有狀態(tài)組件需要依賴于某個(gè)狀態(tài)才能提供服務(wù)。狀態(tài)隱式地將服務(wù)的調(diào)用鏈接到過(guò)去和將來(lái)的調(diào)用。這些組件的容錯(cuò)本質(zhì),就像飛機(jī)的引擎一樣,是否能夠提供運(yùn)行的連續(xù)性。具體來(lái)說(shuō),就是服務(wù)所依賴的狀態(tài)的連續(xù)性。
在本文的剩余部分中,我們將給出每種情況的示例,并解釋在實(shí)踐中實(shí)現(xiàn)容錯(cuò)時(shí)遇到的工程挑戰(zhàn)。
容錯(cuò)設(shè)計(jì)將故障視為常規(guī)
在大型系統(tǒng)中,必須假設(shè)組件故障遲早會(huì)發(fā)生,且有可能即將發(fā)生,并且預(yù)計(jì)組件故障將持續(xù)發(fā)生。
在大型系統(tǒng)中,故障通常是非二進(jìn)制的,我們不能依賴于單個(gè)組件的可靠性,服務(wù)故障具有傳導(dǎo)作用。拜占庭故障就是一個(gè)很經(jīng)典的例子。
例如,某個(gè)組件間歇性的工作,或某個(gè)組件產(chǎn)生誤導(dǎo)性輸出,或者你依賴的外部組件出現(xiàn)故障。這都將影響你整個(gè)系統(tǒng)的可靠性,你的系統(tǒng)能否容忍這些錯(cuò)誤。
容忍非二進(jìn)制故障需要大量的思考、工程實(shí)踐,有時(shí)還需要人為干預(yù)。必須對(duì)每個(gè)潛在故障進(jìn)行識(shí)別和分類,然后必須能夠快速補(bǔ)救,或通過(guò)廣泛的測(cè)試和穩(wěn)健的設(shè)計(jì)決策來(lái)避免。設(shè)計(jì)容錯(cuò)系統(tǒng)的核心挑戰(zhàn)是了解故障的本質(zhì),以及如何檢測(cè)和補(bǔ)救故障,特別是在發(fā)生間歇性故障時(shí),盡可能地持續(xù)為用戶提供服務(wù)。
無(wú)狀態(tài)服務(wù)
無(wú)狀態(tài)服務(wù)對(duì)單個(gè)組件的服務(wù)連續(xù)性沒(méi)有要求。資源的可用性直接轉(zhuǎn)換為組件的可用性。保證資源的可用性是保證無(wú)狀態(tài)服務(wù)可用性的關(guān)鍵。只要有可能,組件都應(yīng)該被設(shè)計(jì)成無(wú)狀態(tài),不僅便于提升可用性,也便于提升可伸縮性。
對(duì)于無(wú)狀態(tài)服務(wù),有多個(gè)獨(dú)立可用的組件來(lái)持續(xù)提供服務(wù)就足夠了。因?yàn)闆](méi)有狀態(tài),單個(gè)組件的耐久性就不值得關(guān)注。
然而,僅擁有足夠的資源是不夠的,你還必須有效地使用它們。你必須要有一種檢測(cè)資源可用性的方法,并在冗余資源之間實(shí)現(xiàn)負(fù)載均衡。
因此,你必須回答以下問(wèn)題:
- 如何在各種各樣的失敗中生存?
- 什么級(jí)別的冗余是可能的?
- 維持這些冗余級(jí)別的資源,性能成本是多少?
- 管理這些冗余級(jí)別的資源,運(yùn)營(yíng)成本是多少?
由此產(chǎn)生的權(quán)衡如下:
- 實(shí)現(xiàn)用戶對(duì)于高可用性的需求
- 經(jīng)營(yíng)成本
- 現(xiàn)實(shí)世界中,使之成為可能的工程可行性
冗余組件以及它們的依賴關(guān)系必須以獨(dú)立的方式進(jìn)行設(shè)計(jì)、配置和操作。簡(jiǎn)單的數(shù)學(xué)公式是:隨著冗余級(jí)別的增加,在統(tǒng)計(jì)學(xué)上,獨(dú)立組件的故障,使整個(gè)系統(tǒng)發(fā)生災(zāi)難性故障的幾率將呈指數(shù)級(jí)降低。
在Ably,為了提高系統(tǒng)的可用性,我們將組件分配到多個(gè)可用性區(qū)域,以防止單個(gè)可用性區(qū)域出現(xiàn)故障。對(duì)于AWS服務(wù)來(lái)說(shuō),這很容易實(shí)現(xiàn)。有時(shí)多個(gè)可用性區(qū)域(AZ)也會(huì)同時(shí)出現(xiàn)故障;有時(shí)可能存在本地連接問(wèn)題,無(wú)法訪問(wèn)該區(qū)域;有時(shí),某個(gè)地區(qū)可能存在容量限制,無(wú)法支持該地區(qū)的所有服務(wù)。因此,我們還通過(guò)在多個(gè)地區(qū)(region)提供服務(wù)來(lái)提高服務(wù)可用性。
建立跨多個(gè)地區(qū)的冗余并不像支持多個(gè)區(qū)域那么簡(jiǎn)單。例如,簡(jiǎn)單地用一個(gè)負(fù)載均衡器在各地區(qū)之間分配請(qǐng)求是沒(méi)有意義的,因?yàn)樨?fù)載均衡器本身可能存在于某個(gè)區(qū)域內(nèi),它也可能變得不可用。
相反,我們使用一系列措施來(lái)確??蛻舳苏?qǐng)求在任何時(shí)候都可以被路由到一個(gè)被認(rèn)為是健康的且具有可用服務(wù)的區(qū)域。
有狀態(tài)服務(wù)
在Ably,可靠性意味著有狀態(tài)服務(wù)的業(yè)務(wù)連續(xù)性,這是一個(gè)比可用性要復(fù)雜得多的問(wèn)題。
有狀態(tài)服務(wù)對(duì)狀態(tài)有內(nèi)在的依賴關(guān)系,這種依賴關(guān)系在每次單獨(dú)的服務(wù)調(diào)用中都存在。該狀態(tài)的連續(xù)性轉(zhuǎn)化為服務(wù)的正確性。對(duì)連續(xù)性的要求意味著服務(wù)的容錯(cuò)性,我們可以通過(guò)冗余,以保障在出現(xiàn)故障時(shí)狀態(tài)不會(huì)丟失。通過(guò)共識(shí)機(jī)制來(lái)解決可能的拜占庭故障。
最簡(jiǎn)單的類比是飛機(jī)安全。一架飛機(jī)墜毀是災(zāi)難性的,飛機(jī)必須提供持續(xù)的服務(wù)。如果沒(méi)有這樣做,狀態(tài)就會(huì)丟失,飛機(jī)就會(huì)墜毀。
對(duì)于任何依賴于狀態(tài)的服務(wù),當(dāng)選擇一個(gè)替代服務(wù)時(shí),要求能夠在前一個(gè)服務(wù)中斷的地方繼續(xù)使用新服務(wù)。因此,保存狀態(tài)是必須的,在這些情況下,僅可用性是不夠的。
在Ably,我們?yōu)闊o(wú)狀態(tài)服務(wù)提供足夠的計(jì)算能力,以支持我們所有客戶的可用性需求。然而,對(duì)于有狀態(tài)服務(wù),我們不僅需要提供冗余服務(wù),還需要有特定的機(jī)制來(lái)利用冗余,以支持我們的服務(wù)保證功能的連續(xù)性。
例如,某個(gè)請(qǐng)求在集群中的某個(gè)實(shí)例上運(yùn)行,而該實(shí)例恰巧遇到故障,迫使該請(qǐng)求轉(zhuǎn)移,則必須有適當(dāng)?shù)臋C(jī)制來(lái)確保請(qǐng)求能夠繼續(xù)執(zhí)行。
為達(dá)到繼續(xù)執(zhí)行的目的,這是幾個(gè)層面配合作用的效果。在一個(gè)層面上,必須存在一種機(jī)制,以確保該請(qǐng)求被重新分配給一個(gè)健康的服務(wù)。在另一個(gè)層面上,需要確保重新分配的服務(wù)在前一個(gè)服務(wù)停止的地方繼續(xù)執(zhí)行。此外,每一種服務(wù)本身都是通過(guò)一定程度的冗余來(lái)實(shí)現(xiàn)和操作的,以保證服務(wù)的總體可靠性。
該機(jī)制的有效性直接轉(zhuǎn)化為服務(wù)的有效性。以一個(gè)場(chǎng)景為例:對(duì)于任何待處理的消息,你需要確切地知道該消息的處理結(jié)果,成功或失敗。
當(dāng)客戶端將消息提交給Ably以進(jìn)行發(fā)布時(shí),服務(wù)接受該消息以進(jìn)行發(fā)布,客戶端希望獲得消息的結(jié)果。此時(shí),主要的可用性問(wèn)題是:服務(wù)接受消息或者拒絕消息的時(shí)間分別是多少?
我們最低的可接受時(shí)間是4秒。
如果你想要發(fā)布消息,而我們卻告訴你我們做不到,那么這是一個(gè)可用性缺陷。這不是很好,但你至少知道當(dāng)前我們的服務(wù)不可用。
然而,如果我們成功地回應(yīng),“是的,我們已經(jīng)收到了你的信息”,但我們卻沒(méi)有真正的繼續(xù)執(zhí)行下去,那就是另一種失敗。那這就是我們功能性的問(wèn)題,是可靠性的缺陷。而且在分布式系統(tǒng)中要解決可靠性問(wèn)題要復(fù)雜得多,需要花費(fèi)大量的工程精力和復(fù)雜性來(lái)滿足可靠性要求。
實(shí)現(xiàn)可靠性的架構(gòu)方法
下面闡述了我們?cè)贏bly采用的架構(gòu)方法,如何利用冗余來(lái)處理消息。
哈希一致性
通常,水平伸縮性是通過(guò)分配可伸縮的資源來(lái)實(shí)現(xiàn)的。就無(wú)狀態(tài)服務(wù)而言,我們將服務(wù)部署在不同的地理位置,當(dāng)請(qǐng)求來(lái)臨時(shí),負(fù)載均衡器會(huì)根據(jù)地理位置分配鄰近的服務(wù)或其他優(yōu)化考慮因素來(lái)決定處理請(qǐng)求的服務(wù)。
同時(shí),針對(duì)有狀態(tài)的服務(wù),服務(wù)器的放置特別重要,當(dāng)某一臺(tái)服務(wù)器發(fā)生故障時(shí),不能影響其他服務(wù)器的正常操作,我們可以通過(guò)哈希一致性來(lái)達(dá)到目的。
一個(gè)具體的例子是,我們通過(guò)哈希一致性算法來(lái)決定消息由哪個(gè)服務(wù)器來(lái)處理,同時(shí)盡最大可能,將消息均勻的分配給不同的服務(wù)器進(jìn)行處理。
消息持久化
當(dāng)消息發(fā)布后,消息被處理,返回響應(yīng)(成功或失敗)給調(diào)用方。可靠性意味著消息不能丟失。反過(guò)來(lái)意味著,只有將消息持久化下來(lái),當(dāng)服務(wù)器發(fā)生故障時(shí),消息依然可以被找回,進(jìn)行后續(xù)的處理。
首先,我們?cè)谥辽賰蓚€(gè)不同的可用性區(qū)域(AZs)中記錄消息的接收情況。這是Ably消息持久化的核心:將消息寫(xiě)入多個(gè)位置,并且確保寫(xiě)入消息的過(guò)程是事務(wù)性的。你總能知道消息要么成功寫(xiě)入,要么失敗。有了這一點(diǎn)的保證,就可以保證消息的后續(xù)處理。
確保消息在多個(gè)可用性區(qū)域中被持久化,因此單個(gè)事件或原因不會(huì)導(dǎo)致數(shù)據(jù)丟失。確保多個(gè)位置的寫(xiě)操作是事務(wù)性的,則需要消息持久層中的分布式一致性。
以這種方式構(gòu)建的系統(tǒng),只有在所有可用性區(qū)域同時(shí)發(fā)生故障時(shí)才會(huì)導(dǎo)致系統(tǒng)不可用,但這種概率是極低的。
在我們的數(shù)學(xué)模型中,當(dāng)一個(gè)節(jié)點(diǎn)發(fā)生故障時(shí),且我們已經(jīng)知道了修復(fù)故障所需的時(shí)間,再加上每個(gè)可用性區(qū)域的故障率,以及各個(gè)可用性區(qū)域連續(xù)發(fā)生故障的概率進(jìn)行建模。最后我們得出我們需要8-9個(gè)可用性區(qū)域來(lái)最大化的保證可靠性。
容錯(cuò)性的工程考慮
即使你有了實(shí)現(xiàn)容錯(cuò)性的理論方法,仍然有許多實(shí)際的和系統(tǒng)工程方面的挑戰(zhàn)需要考慮。
分布式一致性
上述機(jī)制,例如哈希一致性,只有在所有服務(wù)器正常工作時(shí)才有效。
這是一個(gè)經(jīng)典的一致性問(wèn)題,集群中的成員,對(duì)自己的身份(主從關(guān)系)達(dá)成一致性, Raft/Paxos是重要的理論保障,但在實(shí)際的網(wǎng)絡(luò)環(huán)境中,特別是,在跨多個(gè)區(qū)域的網(wǎng)絡(luò)中,如果各個(gè)服務(wù)器之間的網(wǎng)絡(luò)延遲過(guò)大,這些算法的有效性就會(huì)下降。
只有當(dāng)所有參與實(shí)體對(duì)集群的拓?fù)湟约懊總€(gè)節(jié)點(diǎn)的狀態(tài)和健康狀況達(dá)成一致時(shí),上述機(jī)制(如角色放置算法)才能有效。
相反,我們同時(shí)使用Gossip協(xié)議,它是最終一致的、容錯(cuò)的,并且可以跨區(qū)域工作。
結(jié)論
容錯(cuò)性的目的是減輕故障對(duì)系統(tǒng)的影響,以便持續(xù)地為客戶提供服務(wù)。
在Ably中,我們將服務(wù)分為有狀態(tài)和無(wú)狀態(tài)兩類。無(wú)狀態(tài)服務(wù)的容錯(cuò)性極強(qiáng),而有狀態(tài)服務(wù)我們需要保證狀態(tài)的連續(xù)性,才能保證服務(wù)的連續(xù)性。協(xié)調(diào)
要實(shí)現(xiàn)容錯(cuò)性系統(tǒng),必須將故障視為常規(guī)事件,而不是異常事件。除了理論的支撐,設(shè)計(jì)容錯(cuò)系統(tǒng)還涉及許多系統(tǒng)工程挑戰(zhàn)。這包括基礎(chǔ)設(shè)施可用性和可伸縮性問(wèn)題,以及分布式一致性問(wèn)題,如何協(xié)調(diào)全球所有節(jié)點(diǎn)的網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu),以及不可預(yù)測(cè)/難以檢測(cè)的節(jié)點(diǎn)健康狀態(tài)。
Ably平臺(tái)是根據(jù)這些原則從頭設(shè)計(jì)的,其目標(biāo)是提供一流的企業(yè)解決方案。這就是為什么我們可以自信地提供可用性和可靠性服務(wù)的保證,同時(shí)保證容錯(cuò)性。