您好,登錄后才能下訂單哦!
這篇文章主要講解了“DLedger的Jepsen測試方法是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“DLedger的Jepsen測試方法是什么”吧!
分布式系統面臨的挑戰
Is it better to be alive and wrong or right and dead?隨著計算機技術的發展,系統架構從集中式演進到分布式。分布式系統相對于單臺機器來說提供了更好的可擴展性,容錯性以及更低的延遲,但在單臺計算機上運行軟件和分布式系統上運行軟件卻有著根本的區別,其中一點便是單臺計算機上運行軟件,錯誤是可預測的。當硬件沒有故障時,運行在單臺計算機的軟件總是產生同樣的結果;而硬件如果出現問題,那么后果往往是整個系統的故障。因此,對于單體系統來說,要么功能完好且正確,要么完全失效,而不是介于兩者之間。
而分布式系統則復雜的多。分布式系統涉及到多個節點和網絡,因而存在部分失效的問題。分布式系統中不可靠的網絡會導致數據包可能會丟失或任意延遲,不可靠的時鐘導致某節點可能會與其他節點不同步 ,甚至一個節點上的進程可能會在任意時候暫停一段相當長的時間(比如由于垃圾收集器導致)而被宣告死亡,這些都給分布式系統帶來了不確定性和不可預測性。事實上,這些問題在分布式系統中是無法避免的,就像著名的CAP理論中提出的,P(網絡分區)是永遠存在的,而不是可選的。
既然分布式系統中故障是無法避免的,那么處理故障最簡單的方法便是讓整個服務失效,讓應用“正確地死去”,但這并不是所有應用都能接受。故障轉移企圖解決該問題,當故障發生時將其中一個從庫提升為主庫,使新主庫仍然對外提供服務。但是主從數據不一致、腦裂等問題可能會讓應用“錯誤地活著”。代碼托管網站Github在一場事故中,就因為一個過時的MySQL從庫被提升為主庫 ,造成MySQL和 Redis中數據產生不一致,最后導致一些私有數據泄漏到錯誤的用戶手中 。為了減輕故障帶來的影響,我們需要通過某種手段來確保數據的一致性,而如何驗證大規模分布式系統在故障下依然正確和穩定(可靠性)成為了新的難題。
可靠性驗證
分布式系統可靠性的驗證可以采用形式化規范來進行,比如TLA+,但是這樣的驗證需要大量的特定理論知識。另一個方式是通過測試來驗證,但普通的單元測試和集成測試無法覆蓋到一些只有在高并發或者故障發生時才會出現的邊緣情況,這些給分布式系統測試帶來了新的挑戰。
混沌工程的出現帶來了新的驗證思路,企業需要在測試階段發現問題,通過“蓄意”引發故障來確保容錯機制不斷運行并接受考驗,從而提高故障自然發生時系統能正確處理的信心。出身于SRE的Pavlos Ratis,在自己的GitHub 倉庫awesome-chaos-engineering ,維護了與混沌工程相關的書籍、工具、論文、博客、新聞資訊、會議、論壇和 Twitter 賬號。另外,故障注入后,除了觀察系統的可用性,還需要保證系統提供的服務是正確的,也就是系統仍然需要符合預期的一致性,Jepsen目前被認為是工程領域在一致性驗證方面的最佳實踐(下圖展示了Jepsen可驗證的一致性模型)。
Jepsen能在特定故障下驗證系統是否滿足一致性,在過去5年里,Kyle Kingsbury已經幫助無數的早期分布式系統進行過測試,比如Redis、Etcd、Zookeeper等。Jepsen系統如下圖所示,其由 6 個節點組成,一個控制節點,五個DB 節點。控制節點可以通過SSH登錄到DB節點,通過控制節點的控制,可以在DB節點完成分布式系統的部署,組成一個待測試的集群。測試開始后,控制節點會創建一組進程,進程包含了待測試分布式系統的客戶端。另一個Generator進程產生每個客戶端執行的操作,并將操作應用于待測試的分布式系統。每個操作的開始和結束以及操作結果記錄在歷史記錄中。同時,一個特殊進程Nemesis將故障引入系統。測試結束后,Checker分析歷史記錄是否正確,是否符合一致性。
Jepsen一方面提供了故障注入的手段,能模擬各種各樣的故障,比如網絡分區,進程崩潰、CPU超載等。另一方面,它提供了各種校驗模型,比如Set、Lock、Queue等來檢測各種分布式系統在故障下是否仍然滿足所預期的一致性。通過Jepsen測試,能發現分布式系統在極端故障下的隱藏錯誤,從而提高分布式系統的容錯能力。因此Jepsen測試被應用到許多分布式數據庫或分布式協調服務集群的可靠性檢測中,成為驗證分布式系統一致性驗證的重要手段。而現在我們以基于日志的分布式存儲庫DLedger和分布式消息隊列RocketMQ為例,介紹Jepsen測試在分布式消息系統中的應用。
DLedger的Jepsen測試
DLedger是一個基于raft的java庫,用于構建高可用性、高持久性、強一致性的commitlog。如下圖所示,DLedger去掉了raft協議中狀態機的部分,但基于Raft協議保證commitlog是一致的,并且是高可用的。
在對DLedger進行Jepsen測試之前,首先需要明確DLedger需要滿足怎樣的一致性。在Jepsen測試中,許多基于raft的分布式應用都采用線性一致性對系統進行驗證。線性一致性是最強的一致性模型之一,滿足線性一致性的系統,能提供一些唯一性約束的服務,比如分布式鎖,選主等。但從DLedger的定位來看,它是一個Append only的日志系統,并不需要如此嚴格的一致性,數據的最終一致性更加符合我們對DLedger在故障下的正確性要求。因此采用Jepsen的Set測試對DLedger在各種故障下的一致性進行檢測。
Set測試流程如下圖所示,主要分為兩個階段。第一階段由不同的客戶端并發地向待測試集群添加不同的數據,中間會進行故障注入。第二階段,向待測試的集群進行一次最終讀取,獲得讀取的結果集。最后驗證每一個成功添加的元素都在最終結果集中,并且最終的結果集也僅包含企圖添加的元素。
在實際測試中,我們開啟30個客戶端進程并發地向待測試的DLedger集群添加連續不重復的數字,中間會引入特定故障,比如非對稱網絡分區,隨機殺死節點等。故障引入的間隔時間是30s,即30s正常運行,30s故障注入,一直循環,整個階段一共持續600s。并發寫階段結束以后,執行最終的讀取,獲得結果集并進行校驗。
故障注入方面,我們測試以下幾種故障注入:
partition-random-node和partition-random-halves故障是模擬常見的對稱網絡分區。
kill-random-processes和crash-random-nodes故障是模擬進程崩潰,節點崩潰的情況。
hammer-time故障是模擬一些慢節點的情況,比如發生Full GC、OOM等。
bridge和partition-majorities-ring模擬比較極端的非對稱網絡分區。
我們以隨機網絡分區故障partition-random-halves為例,分析測試結果。在測試完成后,日志中會出現如下圖所示的結果:
可以看到測試過程中30個客戶端一共發送了167354個數據(attempt-count),add成功返回167108個數據(acknowledged-count),實際成功添加167113個數據(ok-count),有5個由于請求超時或者多數認證超時導致無法確定是否添加成功,但卻出現在最終讀取結果集中的數據(recovered-count)。由于lost-count=0并且unexpected-count=0,因此最終一致性驗證結果是通過的。 以圖表的形式更好分析DLedger集群在測試過程中的表現情況。客戶端對DLedger集群每一次操作的時延如下圖所示。
其中藍色框表示數據添加成功,紅色框表示數據添加失敗,黃色框表示不確定是否數據添加成功,圖中灰色部分表示故障注入的時間段。可以看出一些故障注入時間段造成了集群短暫的不可用,一些故障時間段則沒有,這是合理的。由于是隨機網絡分區,所以只有當前leader被隔離到少數節點區域才會造成集群重新選舉,但即使造成集群重新選舉,在較短時間內,DLedger集群也會恢復可用性。此外,可以看到由于DLedger對對稱網絡分區有較好的容錯設計,每次故障恢復后,集群不會發生重新選舉。
下圖展示了DLedger在測試過程中時延百分位點圖。
可以看到除了在一些故障引入后造成集群重新選舉的時間段,時延升高,在其他的時間段,Dledger集群表現穩定,95%的數據添加延遲在5ms以下,99%的數據添加延遲在10ms以下。DLedger在隨機對稱網絡分區故障注入下,表現穩定,符合預期。
除了隨機對稱網絡分區,DLedger在其他5種故障注入下也均通過了Set測試的一致性驗證,證明了DLedger對網絡分區,進程、節點崩潰等故障的容錯能力。
RocketMQ的Jepsen測試
Apache RocketMQ是一個具有低延遲、高性能、高可靠性和靈活可擴展性的分布式消息隊列。RocketMQ從4.5.0版本之后支持DLedger方式部署,使單組broker具有故障轉移能力,具有更好的可用性和可靠性。現在我們用Jepsen來檢測RocketMQ DLedger部署模式的容錯能力。
首先依舊需要明確RocketMQ在故障下需要滿足怎樣的一致性。Jepsen為分布式系統提供了total-queue的測試,total-queue測試需要系統滿足入隊的數據必須出隊,也就是消息的傳輸必須滿足at-least-once。這符合我們對RocketMQ在故障下正確性要求,因此采用total-queue對RocketMQ進行Jepsen測試。
total-queue測試如下圖所示,主要分為兩個階段。第一階段客戶端進程并發地向集群隨機調用入隊和出隊操作,入隊和出隊操作比例各占一半,中間會注入故障。第二階段,為了保證每一個數據都出隊,客戶端進程調用drain操作,抽干隊列。
在實際的測試過程中,我們開啟4個客戶端進程并發地向待測試的RocketMQ集群進行入隊和出隊操作,中間會引入特定故障。故障注入間隔時間是200s,整個階段一共持續1小時。第一階段結束以后,客戶端執行drain操作,抽干隊列。
依舊采用上文所述的六種故障注入進行測試,以隨機殺死節點故障為例來分析測試結果(為了保證殺死節點個數不會導致整個集群不可用,代碼保證每次故障注入只殺死少數個節點),測試完成后,出現如下圖所示結果:
可以看到測試過程中30個客戶端一共試圖入隊65947個數據(attempt-count),入隊成功返回64390個數據(acknowledged-count),實際成功入隊64390個數據(ok-count),無重復出隊的數據,因此故障下的一致性驗證是通過的。
我們以圖表形式更好的分析故障下RocketMQ的表現。下圖是客戶端對RocketMQ集群每一次操作的時延圖。
其中紅色小三角形表示入隊失敗,如果一段時間內存在大量的紅色小三角形則表示該時間段系統不可用,從圖中可以發現在故障注入(灰色區域)初期存在一些系統不可用的時間段,這是故障引發集群重新選舉造成的,一段時間后集群仍能恢復可用性。但是可以發現在故障恢復后,也存在系統不可用的時間段,這并不符合預期。
通過日志排查發現,故障恢復后集群不可用的時間幾乎都在30秒左右,這正是broker向nameserver的注冊間隔。進一步排查發現,這段時間內nameserver中master broker路由信息出現了丟失。原來在故障恢復后,被殺死的broker進程進行重啟,此時默認brokerId為零,在brokerId被修改之前,broker向nameserver進行注冊,從而覆蓋了原本master broker路由信息,造成集群在該段時間內不可用。對該問題進行修復并重新進行Jepsen測試,重新測試的時延圖如下圖所示。
重新測試的結果表明問題已經被修復,故障恢復后不存在不可用的時間段。通過Jepsen測試,我們發現了RocketMQ DLedger部署模式在故障注入下可用性方面的問題,并從代碼上進行了優化,貢獻給RocketMQ社區。我們也檢測了其他故障注入下RocketMQ的表現情況,均通過了total-queue測試的一致性驗證。
Jepsen測試的一些思考
以DLedger和RocketMQ為例,我們利用Jepsen對分布式消息系統進行了故障下的一致性驗證。在測試過程中,也發現了Jepsen框架存在的一些缺陷。
Jepsen測試無法長時間運行。Jepsen測試長時間運行會產生大量的數據,這導致其校驗階段出現OOM,但在實際場景中,許多深藏的bug需要長時間的壓力測試、故障模擬才能發現,同時系統的穩定性也需要長時間的運行才能被驗證。
Jepsen測試提供的模型還無法完全覆蓋到特定領域。比如在分布式消息領域,Jepsen僅提供了queue和total-queue的測試,來驗證消息系統在故障下是否會出現消息丟失,消息重復。但是對于分布式消息隊列重要的分區順序性、全局順序性、重平衡算法的有效性并未覆蓋到。
分布式消息標準openmessaging社區也試圖解決這些問題,從而提供消息領域更加完備的可靠性驗證。DLedger的Jepsen測試代碼也已經放到openmessaging的openmessaging-dledger-jepsen倉庫下,并提供了Docker啟動模式,方便用戶能快速地在單臺機器上進行測試。
故障所引發的錯誤代價非常大,電商網站的中斷會導致收入和聲譽的巨大損失,云廠商提供的系統發生宕機或故障時,會給用戶或他們的用戶帶來沉痛的代價。如何讓分布式系統在困境(硬件故障、軟件故障、人為錯誤)中仍可正確完成功能,并能達到期望的性能水準,這不僅要從算法設計和代碼實現上解決,還需要利用分布式系統測試工具提前模擬各種故障,從失敗中找到深層的問題,提高系統的容錯能力。這樣才能在意外真的發生時,將意外的損失降到最低。
感謝各位的閱讀,以上就是“DLedger的Jepsen測試方法是什么”的內容了,經過本文的學習后,相信大家對DLedger的Jepsen測試方法是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。