一、什麼是高可用
高可用HA(High Availability)是分散式系統架構設計中必須考慮的因素之一,它通常是指,透過設計減少系統不能提供服務的時間。
假設系統一直能夠提供服務,我們說系統的可用性是100%。如果系統每執行100個時間單位,會有1個時間單位無法提供服務,我們說系統的可用性是99%。
很多公司的高可用標的是4個9,也就是99.99%,這就意味著,系統的年停機時間為8.76個小時。
百度的搜尋首頁,是業內公認高可用保障非常出色的系統,甚至人們會透過 www.baidu.com 能不能訪問來判斷“網路的連通性”,百度高可用的服務讓人留下啦“網路通暢,百度就能訪問”,“百度打不開,應該是網路連不上”的印象,這其實是對百度HA最高的褒獎。
二、如何保障系統的高可用
我們都知道,單點是系統高可用的大敵,單點往往是系統高可用最大的風險和敵人,應該儘量在系統設計的過程中避免單點。方法論上,高可用保證的原則是“叢集化”,或者叫“冗餘”:只有一個單點,掛了服務會受影響;如果有冗餘備份,掛了還有其他backup能夠頂上。
保證系統高可用,架構設計的核心準則是:冗餘。
有了冗餘之後,還不夠,每次出現故障需要人工介入恢復勢必會增加系統的不可服務實踐。所以,又往往是透過“自動故障轉移”來實現系統的高可用。
接下來我們看下典型網際網路架構中,如何透過冗餘+自動故障轉移來保證系統的高可用特性。
三、常見的網際網路分層架構
常見網際網路分散式架構如上,分為:
- 客戶端層:典型呼叫方是瀏覽器browser或者手機應用APP;
- 反向代理層:系統入口,反向代理;
- 站點應用層:實現核心應用邏輯,傳回html或者json;
- 服務層:如果實現了服務化,就有這一層;
- 資料-快取層:快取加速訪問儲存;
- 資料-資料庫層:資料庫固化資料儲存;
整個系統的高可用,又是透過每一層的冗餘+自動故障轉移來綜合實現的。
四、分層高可用架構實踐
【客戶端層->反向代理層】的高可用
【客戶端層】到【反向代理層】的高可用,是透過反向代理層的冗餘來實現的。以nginx為例:有兩臺nginx,一臺對線上提供服務,另一臺冗餘以保證高可用,常見的實踐是keepalived存活探測,相同virtual IP提供服務。
自動故障轉移:當nginx掛了的時候,keepalived能夠探測到,會自動的進行故障轉移,將流量自動遷移到shadow-nginx,由於使用的是相同的virtual IP,這個切換過程對呼叫方是透明的。
【反向代理層->站點層】的高可用
【反向代理層】到【站點層】的高可用,是透過站點層的冗餘來實現的。假設反向代理層是nginx,nginx.conf裡能夠配置多個web後端,並且nginx能夠探測到多個後端的存活性。
自動故障轉移:當web-server掛了的時候,nginx能夠探測到,會自動的進行故障轉移,將流量自動遷移到其他的web-server,整個過程由nginx自動完成,對呼叫方是透明的。
【站點層->服務層】的高可用
【站點層】到【服務層】的高可用,是透過服務層的冗餘來實現的。“服務連線池”會建立與下游服務多個連線,每次請求會“隨機”選取連線來訪問下游服務。
自動故障轉移:當service掛了的時候,service-connection-pool能夠探測到,會自動的進行故障轉移,將流量自動遷移到其他的service,整個過程由連線池自動完成,對呼叫方是透明的(所以說RPC-client中的服務連線池是很重要的基礎元件)。
【服務層>快取層】的高可用
【服務層】到【快取層】的高可用,是透過快取資料的冗餘來實現的。
快取層的資料冗餘又有幾種方式:第一種是利用客戶端的封裝,service對cache進行雙讀或者雙寫。
快取層也可以透過支援主從同步的快取叢集來解決快取層的高可用問題。
以redis為例,redis天然支援主從同步,redis官方也有sentinel哨兵機制,來做redis的存活性檢測。
自動故障轉移:當redis主掛了的時候,sentinel能夠探測到,會通知呼叫方訪問新的redis,整個過程由sentinel和redis叢集配合完成,對呼叫方是透明的。
說完快取的高可用,這裡要多說一句,業務對快取並不一定有“高可用”要求,更多的對快取的使用場景,是用來“加速資料訪問”:把一部分資料放到快取裡,如果快取掛了或者快取沒有命中,是可以去後端的資料庫中再取資料的。
這類允許“cache miss”的業務場景,快取架構的建議是:
將kv快取封裝成服務叢集,上游設定一個代理(代理可以用叢集冗餘的方式保證高可用),代理的後端根據快取訪問的key水平切分成若干個實體,每個實體的訪問並不做高可用。
快取實體掛了遮蔽:當有水平切分的實體掛掉時,代理層直接傳回cache miss,此時快取掛掉對呼叫方也是透明的。key水平切分實體減少,不建議做re-hash,這樣容易引發快取資料的不一致。
【服務層>資料庫層】的高可用
大部分網際網路技術,資料庫層都用了“主從同步,讀寫分離”架構,所以資料庫層的高可用,又分為“讀庫高可用”與“寫庫高可用”兩類。
【服務層>資料庫層“讀”】的高可用
【服務層】到【資料庫讀】的高可用,是透過讀庫的冗餘來實現的。
既然冗餘了讀庫,一般來說就至少有2個從庫,“資料庫連線池”會建立與讀庫多個連線,每次請求會路由到這些讀庫。
自動故障轉移:當讀庫掛了的時候,db-connection-pool能夠探測到,會自動的進行故障轉移,將流量自動遷移到其他的讀庫,整個過程由連線池自動完成,對呼叫方是透明的(所以說DAO中的資料庫連線池是很重要的基礎元件)。
【服務層>資料庫層“寫”】的高可用
【服務層】到【資料庫寫】的高可用,是透過寫庫的冗餘來實現的。
以mysql為例,可以設定兩個mysql雙主同步,一臺對線上提供服務,另一臺冗餘以保證高可用,常見的實踐是keepalived存活探測,相同virtual IP提供服務。
自動故障轉移:當寫庫掛了的時候,keepalived能夠探測到,會自動的進行故障轉移,將流量自動遷移到shadow-db-master,由於使用的是相同的virtual IP,這個切換過程對呼叫方是透明的。
五、總結
高可用HA(High Availability)是分散式系統架構設計中必須考慮的因素之一,它通常是指,透過設計減少系統不能提供服務的時間。
方法論上,高可用是透過冗餘+自動故障轉移來實現的。
整個網際網路分層系統架構的高可用,又是透過每一層的冗餘+自動故障轉移來綜合實現的,具體的:
- 【客戶端層】到【反向代理層】的高可用,是透過反向代理層的冗餘實現的,常見實踐是keepalived + virtual IP自動故障轉移;
- 【反向代理層】到【站點層】的高可用,是透過站點層的冗餘實現的,常見實踐是nginx與web-server之間的存活性探測與自動故障轉移;
- 【站點層】到【服務層】的高可用,是透過服務層的冗餘實現的,常見實踐是透過service-connection-pool來保證自動故障轉移;
- 【服務層】到【快取層】的高可用,是透過快取資料的冗餘實現的,常見實踐是快取客戶端雙讀雙寫,或者利用快取叢集的主從資料同步與sentinel保活與自動故障轉移;更多的業務場景,對快取沒有高可用要求,可以使用快取服務化來對呼叫方遮蔽底層複雜性;
- 【服務層】到【資料庫“讀”】的高可用,是透過讀庫的冗餘實現的,常見實踐是透過db-connection-pool來保證自動故障轉移;
- 【服務層】到【資料庫“寫”】的高可用,是透過寫庫的冗餘實現的,常見實踐是keepalived + virtual IP自動故障轉移;
末了,希望文章的思路是清晰的,希望大家對高可用的概念和實踐有個系統的認識,感謝大家。