對高可用系統的一點理解

語言: CN / TW / HK

什麼是高可用

簡單來講就是我們系統如何提高對外的服務時間,想要系統達到100%可用基本是不太可能的,也內有一個專門的衡量標準SLA(全稱:Service Level Agreement),也就是有幾個9的高可用性:

  • 90% (1個9):系統全年有36.5天不可用;
  • 99% (2個9):系統全年有3.65天不可用;
  • 99.9% (3個9):系統全年有8.76小時不可用;
  • 99.99% (4個9):系統全年有52.56分不可用;
  • 99.999% (5個9):系統全年有5.26分不可用; 每家公司都在為提高9的個數而努力,當然這不能只靠研發部門就能做到的,需要所有部門的相互配合包括:測試,運維,資料中心,基礎架構等等;本文僅從研發的角度來看看如何提高系統的可用性。

如何提高

想要提高系統的可用性,我們需要整合多種手段來保證,當然在使用各種手段之前,作為研發人員我們首先是要保證自己程式碼的高質量,這是一切的前提,如果不能保證程式碼的質量,再多高可用的手段都是徒勞;下面具體看看都有哪些常用手段;

負載均衡

高可用很重要的一個手段就是避免單點,為啥要避免單點,因為每臺機器都會有出問題的概率,當某一臺機器出現問題,其他機器節點同樣可以提供相同的服務,來保證系統的可用性;從整個系統來看,每一層都需要避免單點,從系統的接入層到服務層到最後的資料層都避免單點的情況下才能保證整個系統的高可用;這時候負載均衡器就起了至關重要的功能,一個負載均衡器包含如下一些核心功能:

  • 操作單元配置;
  • 負載均衡演算法;
  • 失敗重試;
  • 健康檢查;

負載均衡器一方面避免了單點的出現,另一方面多個節點共同提供服務,提供整個系統的能力;每一層都有各自的負載均衡器:

  • 接入層:最常見的就是Nginx了,通過配置檔案簡單配置一下即可,提供了多種負載均衡演算法選擇,健康檢查等功能;
  • 服務層:服務層主要的就是微服務框架比如DubboSpring Cloud等,內部都集成了負載均衡策略,使用起來也是非常方便;
  • 資料層:這一層相比其他層每個節點是有狀態的,不能簡單的橫向擴充套件,一般的做法是為資料做分片處理,每個節點存放部分資料,同時每個節點提供備節點,出現問題可以實時切換。

隔離

隔離在為了系統在發生異常時,能將此異常限定在一定範圍之內,不會產生蝴蝶效應,導致整個系統不可用;隔離的手段有很多比如執行緒隔離、程序隔離、叢集隔離、機房隔離等等,這裡重點看一下離開發人員更近的執行緒隔離;

看一個最常見的場景,在微服務架構下,某一條呼叫鏈中的某一個服務出現問題,導致執行緒阻塞,然後阻塞越積越多,佔滿所有的io執行緒,最終當前服務無法接受資料,直至奔潰;這時候執行緒隔離就起作用了,可以將io執行緒和業務執行緒分開,這樣業務執行緒出現問題不至於影響到io執行緒;

一些框架提供了自己的執行緒模型,比如Dubbo的Dispatcher排程器可以配置訊息的處理執行緒,包含了多種處理方式:

  • all 所有訊息都派發到執行緒池,包括請求,響應,連線事件,斷開事件,心跳等。
  • direct 所有訊息都不派發到執行緒池,全部在 IO 執行緒上直接執行。
  • message 只有請求響應訊息派發到執行緒池,其它連線斷開事件,心跳等訊息,直接在 IO 執行緒上執行。
  • execution 只有請求訊息派發到執行緒池,不含響應,響應和其它連線斷開事件,心跳等訊息,直接在 IO 執行緒上執行。
  • connection 在 IO 執行緒上,將連線斷開事件放入佇列,有序逐個執行,其它訊息派發到執行緒池。

當然可以使用第三方工具來實現隔離,比如SentinelHystrix;提供了執行緒池隔離、訊號量隔離;

限流

我們每個系統其實都有一個處理峰值,當接近峰值繼續接受請求的時候,會導致整個系統響應緩慢;為了保護系統,需要拒絕處理過載的請求,這時候就需要用到限流 ;常見的限流演算法:

  • 令牌桶限流
  • 漏桶限流
  • 計數器限流

一般我們做限流可以在接入層限流,也可以在業務層做限流,下面簡單看一下都有哪些限流工具:

  • 接入層限流:OpenRestynginx的基礎上做了功能擴充,其中有一項就是限流模組,使用lua-resty-limit-traffic模組進行限流;
  • 業務層限流:業務層可以做程序內的限流、也可以做分散式限流;程序內限流可以直接使用guava來實現,內建了很多限流演算法的實現;分散式限流主流使用Redis+luaOpenResty+lua來實現;

降級

當我們的系統出現訪問量大增,比如遇到大促的情況,如果出現系統資源不夠用的情況下,可以優先把資源給核心功能,對非核心功能可以做降級處理,保證核心功能的可用性,這是一種有損的手段;常見的降級手段:

  • 自動開關降級:可以通過系統自動做統計然後做降級處理,比如超時降級、統計失敗次數降級等;Sentinel提供了多種自動降級策略,可以通過控制檯直接配置;
  • 人工開關降級:給非核心功能模組配置降級開關,比如遇到大促期間,可以手動開啟開關實現降級;
  • 讀服務降級:對於一些一致性要求不高的場景,讀服務可能在每一層都做快取比如:接入層快取、應用層本地快取、分散式快取、DB/RPC服務,可以根據實際情況做讀降級處理;
  • 寫服務降級:寫服務做降級處理一般都是做非同步化處理,比如將資料寫入MQ,然後接收MQ資料再寫入資料庫;

超時重試

為什麼要設定超時時間,因為如果不設定超時時間,可能因為某個請求無法即時響應導致整個鏈路處於長時間等待狀態,這種請求如果過多,直接導致整個系統癱瘓,丟擲超時異常其實也是及時止損;系統的每一層幾乎都可以設定超時時間比如:資料庫超時、快取超時、RPC超時、閘道器超時、Web超時、Httpclient超時等;所以對於開發人員來說超時時間的設定至關重要;

重試往往伴隨著超時一起出現,因為超時可能是因為某些特殊原因導致暫時性的請求失敗,也就是說重試是有可能出現請求再次成功的;限制一定的重試次數(常見設定為2次),也是很有必要的;

其他

除了以上幾種開發人員比較關注的高可用手段,還有一些其他的手段:

  • 回滾機制:當上線新功能時,我們需要提供部署版本的回滾機制,這樣如果生產出現問題,可以快速回滾將影響降到最低;資料回滾機制,遊戲行業遇到重大問題時,會使用回檔處理;程式碼版本回滾,回滾到上一個穩定的版本;
  • 監控系統:對系統做全鏈路監控,保證相關人員可以第一時間發現系統問題;
  • 壓測預案:電商系統一般在大促之前會對系統進行全鏈路壓測,並對出現的問題提供應急預案;
  • 灰度釋出:當要上線新功能,首先只是更新少量的服務節點,通過路由權重,讓少部分使用者體驗新版本,如果沒有什麼問題,再更新所有服務節點;這樣可以在出現問題把影響面降到最低,保證了系統的可用性;
  • 攻防演練...

總結

以上介紹了要想實現一個高可用系統的各種手段,但不是說我們實現一個高可用的系統,就要把上面的手段一股腦的實現一遍;需要根據不同的系統型別、處在何種階段、以及不同的系統模組等進行對應的取捨。

更多

限流淺析

灰度釋出淺析

負載均衡淺析

超時與重試淺析

執行緒隔離淺析

感謝關注

可以關注微信公眾號「回滾吧程式碼」,第一時間閱讀,文章持續更新;專注Java原始碼、架構、演算法和麵試。