高并发系统—高可用
文章出自:阿里巴巴十億級并發(fā)系統(tǒng)設計(2021版)
鏈接:https://pan.baidu.com/s/1lbqQhDWjdZe1CBU-6U4jhA?提取碼:8888?
目錄
高可用性(High Availability,HA)
可用性的度量
高可用系統(tǒng)設計的思路
課程小結
開課之后,有同學反饋說課程中偏理論知識的講解比較多,希望看到實例。我一直關注這些聲音,也感謝你提出的建議,在 04 講的開篇,我想對此作出一些回應。
在課程設計時,我主要想用基礎篇中的前五講內容帶你了解一些關于高并發(fā)系統(tǒng)設計的基本 概念,期望能幫你建立一個整體的框架,這樣方便在后面的演進篇和實戰(zhàn)篇中對涉及的知識 點做逐一的展開和延伸。比方說,本節(jié)課提到了降級,那我會在運維篇中以案例的方式詳細 介紹降級方案的種類以及適用的場景,之所以這么設計是期望通過前面少量的篇幅把課程先串起來,以點帶面,逐步展開。 當然,不同的聲音是我后續(xù)不斷優(yōu)化課程內容的動力,我會認真對待每一條建議,不斷優(yōu)化 課程,與你一起努力、進步。
接下來,讓我們正式進入課程。 本節(jié)課,我會繼續(xù)帶你了解高并發(fā)系統(tǒng)設計的第二個目標——高可用性。你需要在本節(jié)課 對提升系統(tǒng)可用性的思路和方法有一個直觀的了解,這樣,當后續(xù)對點講解這些內容時,?能馬上反應過來,你的系統(tǒng)在遇到可用性的問題時,也能參考這些方法進行優(yōu)化。
高可用性(High Availability,HA)
高可用性(High Availability,HA)是你在系統(tǒng)設計時經常會聽到的一個名詞,它指的是系統(tǒng)具備較高的無故障運行的能力。
我們在很多開源組件的文檔中看到的 HA 方案就是提升組件可用性,讓系統(tǒng)免于宕機無法服務的方案。比如,你知道 Hadoop 1.0 中的 NameNode 是單點的,一旦發(fā)生故障則整個集群就會不可用;而在 Hadoop2 中提出的 NameNode HA 方案就是同時啟動兩個 NameNode,一個處于 Active 狀態(tài),另一個處于 Standby 狀態(tài),兩者共享存儲,一旦 Active NameNode 發(fā)生故障,則可以將 Standby NameNode 切換成 Active 狀態(tài)繼續(xù)提 供服務,這樣就增強了 Hadoop 的持續(xù)無故障運行的能力,也就是提升了它的可用性。
通常來講,一個高并發(fā)大流量的系統(tǒng),系統(tǒng)出現(xiàn)故障比系統(tǒng)性能低更損傷用戶的使用體驗。 想象一下,一個日活用戶過百萬的系統(tǒng),一分鐘的故障可能會影響到上千的用戶。而且隨著系統(tǒng)日活的增加,一分鐘的故障時間影響到的用戶數(shù)也隨之增加,系統(tǒng)對于可用性的要求也 會更高。
所以今天,我就帶你了解一下在高并發(fā)下,我們如何來保證系統(tǒng)的高可用性,以便 給你的系統(tǒng)設計提供一些思路。
可用性的度量
可用性是一個抽象的概念,你需要知道要如何來度量它,與之相關的概念是:MTBF 和 MTTR。
MTBF(Mean Time Between Failure):是平均故障間隔的意思,代表兩次故障的間隔時間,也就是系統(tǒng)正常運轉的平均時間。這個時間越長,系統(tǒng)穩(wěn)定性越高。
MTTR(Mean Time To Repair):表示故障的平均恢復時間,也可以理解為平均故障時間。這個值越小,故障對于用戶的影響越小。
可用性與 MTBF 和 MTTR 的值息息相關,我們可以用下面的公式表示它們之間的關系: Availability = MTBF / (MTBF + MTTR)這個公式計算出的結果是一個比例,而這個比例代表著系統(tǒng)的可用性。一般來說,我們會使 用幾個九來描述系統(tǒng)的可用性。
其實通過這張圖你可以發(fā)現(xiàn),一個九和兩個九的可用性是很容易達到的,只要沒有藍翔技校 的鏟車搞破壞,基本上可以通過人肉運維的方式實現(xiàn)。
三個九之后,系統(tǒng)的年故障時間從 3 天銳減到 8 小時。到了四個九之后,年故障時間縮減 到 1 小時之內。在這個級別的可用性下,你可能需要建立完善的運維值班體系、故障處理流程和業(yè)務變更流程。你可能還需要在系統(tǒng)設計上有更多的考慮。比如,在開發(fā)中你要考慮,如果發(fā)生故障,是否不用人工介入就能自動恢復。當然了,在工具建設方面,你也需要多加完善,以便快速排查故障原因,讓系統(tǒng)快速恢復。
到達五個九之后,故障就不能靠人力恢復了。想象一下,從故障發(fā)生到你接收報警,再到你打開電腦登錄服務器處理問題,時間可能早就過了十分鐘了。所以這個級別的可用性考察的 是系統(tǒng)的容災和自動恢復的能力,讓機器來處理故障,才會讓可用性指標提升一個檔次。
?
一般來說,我們的核心業(yè)務系統(tǒng)的可用性,需要達到四個九,非核心系統(tǒng)的可用性最多容忍到三個九。在實際工作中,你可能聽到過類似的說法,只是不同級別,不同業(yè)務場景的系統(tǒng)對于可用性要求是不一樣的。
目前,你已經對可用性的評估指標有了一定程度的了解了,接下來,我們來看一看高可用的系統(tǒng)設計需要考慮哪些因素。
高可用系統(tǒng)設計的思路
一個成熟系統(tǒng)的可用性需要從系統(tǒng)設計和系統(tǒng)運維兩方面來做保障,兩者共同作用,缺一不 可。那么如何從這兩方面入手,解決系統(tǒng)高可用的問題呢?
1.?系統(tǒng)設計
“Design for failure”是我們做高可用系統(tǒng)設計時秉持的第一原則。在承擔百萬 QPS 的高并發(fā)系統(tǒng)中,集群中機器的數(shù)量成百上千臺,單機的故障是常態(tài),幾乎每一天都有發(fā)生故障的可能。未雨綢繆才能決勝千里。我們在做系統(tǒng)設計的時候,要把發(fā)生故障作為一個重要的考慮點, 預先考慮如何自動化地發(fā)現(xiàn)故障,發(fā)生故障之后要如何解決。當然了,除了要有未雨綢繆的 思維之外,我們還需要掌握一些具體的優(yōu)化方法,比如failover(故障轉移)、超時控制以 及降級和限流。
一般來說,發(fā)生 failover 的節(jié)點可能有兩種情況:
- 是在完全對等的節(jié)點之間做 failover。
- 是在不對等的節(jié)點之間,即系統(tǒng)中存在主節(jié)點也存在備節(jié)點。
在對等節(jié)點之間做 failover 相對來說簡單些。在這類系統(tǒng)中所有節(jié)點都承擔讀寫流量,并且節(jié)點中不保存狀態(tài),每個節(jié)點都可以作為另一個節(jié)點的鏡像。在這種情況下,如果訪問某一個節(jié)點失敗,那么簡單地隨機訪問另一個節(jié)點就好了。舉個例子,Nginx 可以配置當某一個 Tomcat 出現(xiàn)大于 500 的請求的時候,重試請求另一 個 Tomcat 節(jié)點,就像下面這樣:
?
針對不對等節(jié)點的 failover 機制會復雜很多。比方說我們有一個主節(jié)點,有多臺備用節(jié) 點,這些備用節(jié)點可以是熱備(同樣在線提供服務的備用節(jié)點),也可以是冷備(只作為備份使用),那么我們就需要在代碼中控制如何檢測主備機器是否故障,以及如何做主備切 換。使用最廣泛的故障檢測機制是“心跳”。你可以在客戶端上定期地向主節(jié)點發(fā)送心跳包,也 可以從備份節(jié)點上定期發(fā)送心跳包。當一段時間內未收到心跳包,就可以認為主節(jié)點已經發(fā) 生故障,可以觸發(fā)選主的操作。 選主的結果需要在多個備份節(jié)點上達成一致,所以會使用某一種分布式一致性算法,比方說 Paxos,Raft。
除了故障轉移以外,對于系統(tǒng)間調用超時的控制也是高可用系統(tǒng)設計的一個重要考慮方面。
復雜的高并發(fā)系統(tǒng)通常會有很多的系統(tǒng)模塊組成,同時也會依賴很多的組件和服務,比如說緩存組件,隊列服務等等。它們之間的調用最怕的就是延遲而非失敗,因為失敗通常是瞬時的,可以通過重試的方式解決。而一旦調用某一個模塊或者服務發(fā)生比較大的延遲,調用方就會阻塞在這次調用上,它已經占用的資源得不到釋放。當存在大量這種阻塞請求時,調用方就會因為用盡資源而掛掉。 在系統(tǒng)開發(fā)的初期,超時控制通常不被重視,或者是沒有方式來確定正確的超時時間。
我之前經歷過一個項目,模塊之間通過 RPC 框架來調用,超時時間是默認的 30 秒。平時系統(tǒng)運行得非常穩(wěn)定,可是一旦遇到比較大的流量,RPC 服務端出現(xiàn)一定數(shù)量慢請求的時候,RPC 客戶端線程就會大量阻塞在這些慢請求上長達 30 秒,造成 RPC 客戶端用盡調用線程而掛掉。后面我們在故障復盤的時候發(fā)現(xiàn)這個問題后,調整了 RPC,數(shù)據庫,緩存以及調用第三方服務的超時時間,這樣在出現(xiàn)慢請求的時候可以觸發(fā)超時,就不會造成整體系統(tǒng)雪崩。既然要做超時控制,那么我們怎么來確定超時時間呢?這是一個比較困難的問題。
超時時間短了,會造成大量的超時錯誤,對用戶體驗產生影響;超時時間長了,又起不到作 用。我建議你通過收集系統(tǒng)之間的調用日志,統(tǒng)計比如說 99% 的響應時間是怎樣的,然后依據這個時間來指定超時時間。如果沒有調用的日志,那么你只能按照經驗值來指定超時時間。不過,無論你使用哪種方式,超時時間都不是一成不變的,需要在后面的系統(tǒng)維護過程中不斷地修改。
超時控制實際上就是不讓請求一直保持,而是在經過一定時間之后讓請求失敗,釋放資源給 接下來的請求使用。這對于用戶來說是有損的,但是卻是必要的,因為它犧牲了少量的請求卻保證了整體系統(tǒng)的可用性。
而我們還有另外兩種有損的方案能保證系統(tǒng)的高可用,它們就是降級和限流。
降級是為了保證核心服務的穩(wěn)定而犧牲非核心服務的做法。比方說我們發(fā)一條微博會先經過反垃圾服務檢測,檢測內容是否是廣告,通過后才會完成諸如寫數(shù)據庫等邏輯。反垃圾的檢測是一個相對比較重的操作,因為涉及到非常多的策略匹配,在日常流量下雖然會比較耗時卻還能正常響應。但是當并發(fā)較高的情況下,它就有可能成為瓶頸,而且它也不是發(fā)布微博的主體流程,所以我們可以暫時關閉反垃圾服務檢測,這樣就可以保證主體的流 程更加穩(wěn)定。
限流完全是另外一種思路,它通過對并發(fā)的請求進行限速來保護系統(tǒng)。比如對于 Web 應用,我限制單機只能處理每秒 1000 次的請求,超過的部分直接返回錯誤給客戶端。雖然這種做法損害了用戶的使用體驗,但是它是在極端并發(fā)下的無奈之舉,是短 暫的行為,因此是可以接受的。實際上,無論是降級還是限流,在細節(jié)上還有很多可供探討的地方,我會在后面的課程中, 隨著系統(tǒng)的不斷演進深入地剖析,在基礎篇里就不多說了。
2.?系統(tǒng)運維
在系統(tǒng)設計階段為了保證系統(tǒng)的可用性可以采取上面的幾種方法,那在系統(tǒng)運維的層面又能做哪些事情呢?其實,我們可以從灰度發(fā)布、故障演練兩個方面來考慮如何提升系統(tǒng)的可用性。
你應該知道,在業(yè)務平穩(wěn)運行過程中,系統(tǒng)是很少發(fā)生故障的,90% 的故障是發(fā)生在上線變更階段的。比方說,你上了一個新的功能,由于設計方案的問題,數(shù)據庫的慢請求數(shù)翻了一倍,導致系統(tǒng)請求被拖慢而產生故障。如果沒有變更,數(shù)據庫怎么會無緣無故地產生那么多的慢請求呢?因此,為了提升系統(tǒng)的可用性,重視變更管理尤為重要。而除了提供必要回滾方案,以便在出現(xiàn)問題時快速回滾恢復之外,另一個主要的手段就是灰度發(fā)布。
灰度發(fā)布指的是系統(tǒng)的變更不是一次性地推到線上的,而是按照一定比例逐步推進的。一般情況下,灰度發(fā)布是以機器維度進行的。比方說,我們先在 10% 的機器上進行變更,同時觀察 Dashboard 上的系統(tǒng)性能指標以及錯誤日志。如果運行了一段時間之后系統(tǒng)指標比較 平穩(wěn)并且沒有出現(xiàn)大量的錯誤日志,那么再推動全量變更。
灰度發(fā)布給了開發(fā)和運維同學絕佳的機會,讓他們能在線上流量上觀察變更帶來的影響,是保證系統(tǒng)高可用的重要關卡。
灰度發(fā)布是在系統(tǒng)正常運行條件下,保證系統(tǒng)高可用的運維手段,那么我們如何知道發(fā)生故障時系統(tǒng)的表現(xiàn)呢?這里就要依靠另外一個手段:故障演練。
故障演練指的是對系統(tǒng)進行一些破壞性的手段,觀察在出現(xiàn)局部故障時,整體的系統(tǒng)表現(xiàn)是怎樣的,從而發(fā)現(xiàn)系統(tǒng)中存在的,潛在的可用性問題。
一個復雜的高并發(fā)系統(tǒng)依賴了太多的組件,比方說磁盤,數(shù)據庫,網卡等,這些組件隨時隨地都可能會發(fā)生故障,而一旦它們發(fā)生故障,會不會如蝴蝶效應一般造成整體服務不可用呢?我們并不知道,因此,故障演練尤為重要。
在我來看,故障演練和時下比較流行的“混沌工程”的思路如出一轍,作為混沌工程的鼻祖,Netfix 在 2010 年推出的“Chaos Monkey”工具就是故障演練絕佳的工具。它通過 在線上系統(tǒng)上隨機地關閉線上節(jié)點來模擬故障,讓工程師可以了解,在出現(xiàn)此類故障時會有 什么樣的影響。
當然,這一切是以你的系統(tǒng)可以抵御一些異常情況為前提的。如果你的系統(tǒng)還沒有做到這一 點,那么我建議你另外搭建一套和線上部署結構一模一樣的線下系統(tǒng),然后在這套系統(tǒng)上做 故障演練,從而避免對生產系統(tǒng)造成影響。
課程小結
本節(jié)課我?guī)懔私饬巳绾味攘肯到y(tǒng)的可用性,以及在做高并發(fā)系統(tǒng)設計時如何來保證高可用。 說了這么多,你可以看到從開發(fā)和運維角度上來看,提升可用性的方法是不同的:
開發(fā)注重的是如何處理故障,關鍵詞是冗余和取舍。冗余指的是有備用節(jié)點,集群來頂替出故障的服務,比如文中提到的故障轉移,還有多活架構等等:取舍指的是丟卒保車,保 障主體服務的安全。
從運維角度來看則更偏保守,注重的是如何避免故障的發(fā)生,比如更關注變更管理以及何做故障的演練。
兩者結合起來才能組成一套完善的高可用體系。
你還需要注意的是,提高系統(tǒng)的可用性有時候是以犧牲用戶體驗或者是犧牲系統(tǒng)性能為前提 的,也需要大量人力來建設相應的系統(tǒng),完善機制。所以我們要把握一個度,不該做過度的優(yōu)化。就像我在文中提到的,核心系統(tǒng)四個九的可用性已經可以滿足需求,就沒有必要一味 地追求五個九甚至六個九的可用性。
另外,一般的系統(tǒng)或者組件都是追求極致的性能的,那么有沒有不追求性能,只追求極致的可用性的呢?答案是有的。比如配置下發(fā)的系統(tǒng),它只需要在其它系統(tǒng)啟動時提供一份配置即可,所以秒級返回也可,十秒鐘也 OK,無非就是增加了其它系統(tǒng)的啟動速度而已。但是,它對可用性的要求是極高的,甚至會到六個九,原因是配置可以獲取的慢,但是不能獲 取不到。
我給你舉這個例子是想讓你了解,可用性和性能有時候是需要做取舍的,但如何取舍就要視不同的系統(tǒng)而定,不能一概而論了。
總結