高併發系統設計目標之高可用

高可用(High Availability,HA)

可用性指的是軟件系統在投入使用時可正常提供服務的程度,或能實現其指定系統功能的概率,是衡量系統可以無故障的運行的能力。同樣是一個每秒可以接受1萬次請求的購物系統中,一個隔三岔五的就出現故障,而另外一個可以長時間工作,顯然可以長時間工作的系統的體驗更好。通常來講對於一個高併發大流量的系統,系統的可用性通常比系統性能低下的影響更大,一個日活百萬的系統,1分鐘的故障影響的客戶可能有成百上千,想想是多麼的可拍。而隨着系統的日活的增加,對於可用性的要求就越高。那麼可用性都有什麼衡量標準呢?

我們還經常聽到一個詞可靠性,可靠性是指系統可以無故障地持續運行,是一個持續的狀態。與可用性相反,可靠性是根據時間段而不是任何時刻來進行定義的。如果系統在每小時崩潰1ms,那麼它的可用性就超過99.9999%,但是它還是高度不可靠。與之類似,如果一個系統從來不崩潰,但是每年要停機兩星期,那麼它是高度可靠的,但是可用性只有96%。

可用性衡量標準

我們一般用幾個9來衡量系統的可用性,下表列出了不同的可用性級別下的年故障時間與日故障時間。
在這裏插入圖片描述

從圖中可以看出1個9與2個9下,年故障時間與日故障時間還是比較長的,如果沒有太挫的程序員,基本靠人肉運維就可以實現了。但是當達到3個9與4個9的條件下,年故障時間分別降低到了8小時與52分鐘,日故障時間降低到了1.44分與8.6秒。在如此低的故障時間要求下,想靠人肉運維來實現,顯然是不可能的。想想看等你發現告警到打開電腦解決問題可能已經過了好幾分鐘了。爲此當達到3個9及以上,我們就需要一整套機制來保障系統的可用性了,特別是4個9及以上我們就需要機器能夠自動的處理故障,自動恢復系統的運行。
一般來說對於核心服務,我們至少要做到4個9,對於非核心服務至少要做到3個9。有些服務甚至要求做到6個9。

提升可用性手段

提升系統可用性的手段,一般需要從系統設計與運維的角度出發。下面我們分別從系統設計與運維的角度分別討論。

系統設計

在一個成百上千的大規模機器中,發生故障是常態,爲此我們進行系統設計的時候,就需要考慮如果發生了故障,系統如何恢復。我們要養成「design for failture」的思維。
系統設計我們一般從故障轉移(failover)、超時控制、重試、降級、限流與熔斷的維度出發。
1、故障轉移(failover)
故障轉移我們一般通過冗餘節點來實現,當一個節點出現故障時,我們可以把流量從故障節點導流到其他節點。
對於對等服務,處理起來比較簡單,由於每個節點都是無狀態的,只需要簡單把請求導流到其他的正常節點即可。
對於不對等服務,就比較麻煩,由於每個節點不是對等的,一般備份節點需要複製主節點的狀態,不管是熱備(備份節點同時也提供讀服務)還是冷備(只是備份數據),當主節點故障了,failover機制需要進行主備切換。一般我們通過心跳機制來發現不可用節點。那麼主備切換需要注意什麼呢?主備切換一般需要考慮數據的完整性,選擇數據最完整的備份節點,同時一般還需要保證只有一個主節點能夠對外提供服務,爲此需要使用raft,paxos等分佈式一致性算法保證只有一個主節點。可以看出主備切換一般都比較複雜。

2、超時控制
對於調用方需要設置被調用服務的超時時間。想想如果沒有超時時間,假如服務故障了,那麼調用方的資源得不到釋放並且會導致長時間得不到響應,而長時間無響應,可能還會導致整個調用練上的服務都堵塞在這個調用上,如果有大量的這種情況,那麼可能導致整個系統雪崩。要是打開微博,由於故障導致長時間的停留在打開中的頁面,對於用戶體驗的影響是多麼的大。那超時時間設置的多長呢?對於一個後臺服務超時時間一般不超過200ms,但是在性能篇中說過,整個系統的響應時間最好不超過200ms,如果系統的調用鏈比較長,200ms顯然是太長了。我們在設置超時時間的時候,需要看被調用方的歷史響應時間的,一般可以設置爲99分位的響應時間。如果99分位響應時間較長,應該需要進行性能優化。

3、重試
對於調用方一般需要設置被調用方的重試次數,以提供更高的可用性,即使是4個9的高可用的系統,如果一個請求的調用鏈長度是8,在所有節點都不出故障的概率是99.92%,可用性直接少了一個9。爲此我們一般需要設置重試次數,那麼設置爲多少次呢?對於一個4個9的系統來說,如果設置重試次數爲3次,每次都出問題的概率實在太小了(概率爲0.0001的4次方)。爲此一般設置爲3次即可。

4、降級
降級是爲了保障核心服務的可用性,而捨棄非核心的應用,是一種有損的系統容錯方式。
比如微博系統的反廣告服務,相對於微博的發佈功能,重要性相對差一些。那麼再流量高峯時,反廣告服務可能成爲瓶頸,爲了保障主流程的可用性,可以先臨時關閉反廣告服務。那麼我們如何關閉呢?總不能通過發版實現吧。我們一般通過配置中心來實現,配置中心應該加一個開關,當打開時,調用方直接跳過或者被調用方返回某個固定值。添加開關的時候一定要通過測試並且定期的測試(可以在線上服務流量比較低的時候演練),確定添加的開關配置是有效的。
在比如,在讀數據的場景中,如果降級開關打開,可以返回固定的數據。在輪詢查詢數據庫的場景中,我們可以延長查詢數據庫的時間間隔,以緩解高峯流量時數據庫的壓力。

5、限流
限流則是限制服務的併發訪問以保證系統。比如微博的註冊服務,由於需要訪問數據庫,而數據庫的TPS可能只有1000。那麼我們可以限制註冊服務每秒只能接受比1000小一些的請求如950。限流一定程度上也可以說是一種降級機制。

6、熔斷
熔斷這個引用於電路的保險絲機制,當電路的功率超過一定的閾值,保險絲就會熔斷以保護電路。在服務治理中熔斷指的是當服務的提供方返回連續幾次錯誤或者超時達到一定的閾值就觸發熔斷,則後續的請求不在發給服務提供方而是直接返回錯誤。熔斷是調用方的一種自我保護策略,而限流則是服務的提供方的一種自我保護策略。
服務的調用方需要維護熔斷的三種狀態:關閉、半關閉與打開。其狀態的流轉如下圖
在這裏插入圖片描述

當服務調用失敗或超時的次數達到一定的閾值,熔斷進入打開的狀態,後續的請求調用方直接返回錯誤,這裏還有一個地方需要注意的,當服務返回成功了,應該重置失敗次數;
半打開狀態下,服務的調用方可以發送少量請求到服務提供方,如果調用超時或者失敗,則重新回到打開狀態;如果連續多次請求都成功,則回到關閉狀態。需要注意的是,如果回到關閉狀態時,如果全部流量都請求到被調用方,可能會把服務又打死,服務的提供方應該做好限流。

運維

1、灰度發佈
在線上系統中出現的問題,大部分都是由於發佈新版本導致的,你想要沒有發佈新版本,系統運行的好好的怎麼會突然出現問題呢。爲此我們發佈新版本的時候需要採用灰度發佈的策略。灰度發佈是指系統的變更不是一次性的就全部發布到線上,而是一次發佈部分節點隻影響部分的請求如10%。恢復發佈一般是以節點爲單位的,比如一次發佈10%的節點,然後慢慢的增加發布的節點數,當發佈的過程中出現問題了,則立即回滾服務停止發佈。由於我們引入了灰度發佈,爲此新的變更如果有問題,一般會在發佈的過程中就暴露出來了,從而將影響限制在很小的範圍。

2、故障演練 在一個大規模的系統中,集羣由成百上千的機器構成,在如此大規模的集羣中,某個節點發生故障是常態。如果沒有一種機制定期測試如果某個節點發生故障,我們的集羣是否能夠正常提供服務,那麼當真的發生了故障了,我們往往會不知所措。爲此我們需要引入故障演練。故障演練指的是我們對系統做一些破壞(如隨機地關閉線上節點),以觀察系統的的表現,從而發現潛在的可用性問題。但是如果我們的線上系統還不能抵禦這些異常情況,那麼我們應該在測試環境搭建一套與線上一樣的系統,以便測試。