對於企業來講,隨着規模愈來愈大,整個系統中存在愈來愈多的子系統,每一個子系統又被多個其餘子系統依賴或者依賴於其餘子系統。大部分系統在走到這一步的過程當中,大機率會發生這樣的場景:做爲某個子系統的負責人或者 OnCall 人員,休息的時候都不安穩,內心總是忐忑着系統會不會掛。致使週末不敢長時間出門,晚上睡夢中被電話叫醒,痛苦不堪。docker
那麼,在一個成熟的分佈式系統中,咱們該如何去保證它的可用性呢?迫切的須要解放咱們緊繃的神經。下面,咱們就來看下作高可用的思路和關鍵部分。後端
如何下手作高可用? 在這個時候,咱們的系統全貌大體是這樣的。瀏覽器
由大大小小的多個部分組合而成的一個完整系統,能夠看到包含網關、Web 層、服務層、中間件、基礎設施,這每一層之間又是層層依賴。在如此的一個龐然大物面前作高可用是一個系統化的工程,除了良好的頂層設計規劃外,還須要深刻到細節。因爲雪崩效應的存在,軟件系統是一個完美體現「千里之堤毀於蟻穴」的地方,一個小問題致使整個系統全盤崩塌的案例也不在少數。網絡
因此,首先咱們須要擁有保持懷疑的心態。這個懷疑是指對系統的懷疑,而不是對人的懷疑。人非聖賢孰能無過,何況寫代碼是一個精細活,還不是流水線式的那種。並且,哪怕不是寫代碼的疏忽,其餘諸如網絡、操做系統等異常,甚至一些惡意的攻擊都會致使故障隨時發生。負載均衡
那麼咱們具體應該怎麼作呢?既然故障致使了可用性下降,那麼接下來的工做必然是圍繞解決故障展開。分爲 3 個步驟:故障發現、故障消除、故障善後。框架
故障發現異步
所謂「故障發現」,就是經過技術手段實時採集系統中每一個節點的健康狀態,以及每 2 個節點之間鏈路的健康狀態,包括但不限於調用成功率、響應時間等等。藉此代替咱們的眼睛去盯着整個系統,一旦低於某個設定的閾值,就觸發報警給咱們一個提醒。由於當你的系統中存在成百上千的程序時,靠肉眼去找到發生故障的位置,簡直是天方夜譚。哪怕找到了,也可能已經產生了巨大的損失。分佈式
負責故障發現的解決方案都屬於應用性能管理(APM)範疇。咱們在部署這個「眼睛」的時候,須要考慮到全方位的覆蓋,要包含全部的節點。好比:性能
後端是分佈式系統的主戰場,有進程外和進程內兩個維度的解決方案。 1)進程外的解決方案,例如運用 Zabbix 之類的無探針解決方案,調用系統或者服務自身提供的狀態接口獲取採集數據(以下圖所示),以及對網絡數據包的監聽來獲取網絡性能方面的數據。測試
因爲是進程外的,因此這類方案對咱們的程序是無侵入的,最友好。但弊端也很明顯,監控的粒度太粗,只能進行一些外在的監控。好比能夠發現 CPU 忽然飆高了,可是並不知道可疑的接口是哪一個,更沒法知道是哪行代碼致使的問題。所以,只適合做爲輔助方案。
2)後端的進程內解決方案能夠解決進程外方案的短板,可是因爲須要侵入到應用程序內部,因此對性能和穩定性會帶來必定的影響。關於這類方案咱們有不少的選擇能夠來實現它:能夠同 APP 同樣運用採集 SDK 和 AOP 框架,還能夠經過利用整個系統中的「鏈接」部分來進行,好比一些中間件(數據層訪問框架、服務調用框架等)。
作好了監控,就作好了故障發現一半的工做。另一半是什麼呢?就是故障注入測試(Fault Insertion Test)。咱們須要經過技術手段來主動製造「故障」,以此來提早檢驗系統在各類故障場景下的表現狀況是否符合咱們預期。
監控是一雙眼睛,替你盯着故障,可是咱們不能守株待兔,不然大部分突發的故障都會在生產環境發生。一旦發生就會對經營的業務產生或多或少的影響,甚至看似平靜的系統下,藏着幾個隨時會引爆的炸彈,咱們也不得而知。因此咱們須要主動出擊,主動去製造「故障」來鍛鍊系統。
在實際運用中,故障能夠被注入到軟件,也能夠被注入到硬件。注入到軟件的方式,無外乎這兩種:
若是注入到硬件中就簡單不少,直接運行一段代碼把 CPU、網卡等吃滿便可。
故障注入測試的過程大體是這樣,在故障模型庫中選擇一個模型,而後將該模型對應的故障注入到一個在獨立的環境中運行而且被包裹了一層「炸藥包」的系統,至關於在你指定的地方去「點火」,隨後進行監測並分析結果(以下圖所示)。
故障消除
如今已經可以很容易的發現故障了,咱們就能夠經過綜合運用隔離性、橫向擴展、代理、負載均衡、熔斷、限流、降級等等機制來快速的「掐滅故障」。
分佈式系統的規模越大,耦合越嚴重,各個子系統之間經過網絡鏈接在一塊兒,就如赤壁之戰中的曹軍連在一塊兒的船舶同樣,只要其中一個着火了就會就近蔓延。因此,一旦發現某個子系統掛了,就須要儘快切斷與它的聯繫,保證本身可以不受連累,防止雪崩的發生。
咱們能夠首先運用 docker 之類的技術將每一個應用在運行時的環境層面隔離開來。而後,經過橫向擴展讓每一個應用容許被「Copy」,以此來部署多個副本。接着,結合代理和負載均衡讓這些副本能夠共同對外提供服務,使得每一個應用程序自己先具有「高可用」。最後的三大防護措施,熔斷、限流、降級來快速「掐滅故障」,避免故障在不一樣的應用程序間擴散。
故障善後
「故障消除」避免了級聯故障致使的系統性風險,這時整個分佈式系統已經具有健壯性了。可是對正在使用系統的用戶來講,這些故障仍是可見的,由於會反映成他實際操做中的錯誤提示,甚至致使流程沒法繼續。這對咱們「衣食父母」來講並不友好,最終可能會致使用戶的流失。
因此,咱們應該經過一些補償和緩衝的方式將故障產生的影響降到最低,儘量的去包容故障,讓用戶無感。而且,這些善後工做應該與「故障發現」、「故障消除」一塊兒造成一個完整的體系,以及儘量的自動化。
前面咱們聊到,故障產生的緣由要麼是調用的節點處於異常狀態,要麼是通訊鏈路異常。因此,要作好「故障善後」,就須要在節點之間的鏈接上作文章。根據 CAP 定理、BASE 理論,咱們已經很清楚兩個進程之間的調用方式。一是直接點對點的同步調用,或者是經過一些技術中間層進行異步的調用。
那麼,針對同步調用咱們能夠有兩種方式去實施。
首先是當即重試。不少時候,相同節點的全部副本可能只是因爲網絡緣由,致使其中的某個節點沒法被訪問。那麼,此時若是後端的負載均衡策略只要不是 Hash 類的策略,而且後端服務的方法是無狀態的且支持冪等性的,就能夠立馬重試一次,大機率就能調用成功。不過,這個方案潛在的一個反作用是,若是後端服務整體負載很高,且沒法自動彈性擴容,那麼會進一步加重一些壓力。因此,你能夠增長一個容許被重試的條件,以及爲實際的重試操做增長一個約定。好比,這兩個耗時分別都不能大於 1 秒。
方式二,將能夠容忍最終一致性的同步調用產生的出錯消息進行異步重發。好比,電商網站中提交訂單中所依賴的訂單模塊產生故障,咱們能夠將其暫存到消息隊列中,而後再進行異步的投遞,同時提示給用戶「訂單正在加緊建立中,稍後通知您支付」之類的語句,至少先讓訂單可以下進來。這本質上算得是一個「降級」方案。 若是自己就是一個異步調用,好比最多見的就是發往消息隊列出現異常。由於,一個高可用的消息隊列集羣,大多數狀況下致使消息沒法被投遞的緣由是網絡問題。這個時候,理論上咱們能夠基於每一個應用的本地磁盤部署一個本地 MQ,能夠避免很大一部分這個問題。可是實際每每不會這麼作,由於這麼作的性價比過低,緣由有兩點:
這麼多消息隊列維護成本過高。
若是用到的是消息隊列集羣,自己已具有軟件層面的高可用,因此出現這個問題的機率很低。 因此,這個時候咱們大多會經過定時的任務(job)去進行對帳(數據一致性檢測)。任務(job)的具體實現上儘量作到自動修正,不然通知人工介入。
總結
此次,咱們系統化的梳理了如何來應對「故障」,以此來達到作好高可用的目的。核心觀點就是:保持着懷疑的心態,去發現故障、消除故障、而且爲故障作善後。
至此,咱們有必要開始衡量咱們的高可用到底作的怎麼樣了。就是統計一下整年的故障時間,得出所謂的「幾個 9」的結論。
可是,到目前爲止,咱們在通往幾個 9 的道路上只走了一半。剩下的一半就是討論如何作到無限接近於 100% 的高可用。
你在工做中,還經過哪些方式爲高可用做出過努力呢?主流和非主流均可以說下,一塊兒開開腦洞。 歡迎在下方評論區留言。
看到這裏,說明你喜歡本文,你的轉發,是對我最大的鼓勵!關注亦是支持↓
每日福利資料免費贈送: