高可用架構(gòu):如何做到應(yīng)用升級無感知
十幾年前,我參加阿里巴巴面試的時候,覺得阿里巴巴這樣的網(wǎng)站W(wǎng)eb應(yīng)用開發(fā)簡直小菜,因為我之前是做類似Tomcat這樣的Web容器開發(fā)的,所以面試的時候信心滿滿。
確實,面試官前面的問題都是關(guān)于數(shù)據(jù)結(jié)構(gòu)、操作系統(tǒng)、設(shè)計模式的,也就是我們這個專欄模塊一和模塊二的內(nèi)容。我感覺自己回答得還不錯,所以更加信心滿滿。這時候,面試官忽然提了一個問題:
我們的Web程序每個星期都會發(fā)布一個新版本,但是程序要求7*24小時可用,也就是說,啟動新版本程序替換老程序,進(jìn)行程序升級的時候,程序還在對外提供服務(wù),用戶沒有感覺到停機(jī),我們是怎么做到的呢?
應(yīng)用程序升級必須要用新版本的程序包替代老版本的程序包,并重新啟動程序,這段時間程序是不能對外提供服務(wù)的,用戶請求一定會失敗。但是阿里巴巴讓這段時間的用戶請求依然是成功的。打個比方,就是要在飛機(jī)飛行過程中更換發(fā)動機(jī),還不能讓乘客感覺到。這個問題當(dāng)時完全不在我的知識范圍之內(nèi),但是我知道這個需求場景是真實存在的,而且確實應(yīng)該是可以做到的,可是我完全不知道是怎么做到的。
面試官看我瞠目結(jié)舌,笑著問我,想不想知道答案。我立刻回答說想知道,結(jié)果面試官跟我說,加入我們團(tuán)隊你就知道了。
這其實是一個關(guān)于互聯(lián)網(wǎng)應(yīng)用可用性的問題。我們知道,Web應(yīng)用在各種情況下都有可能不可訪問,也就是不可用。各種硬件故障,比如應(yīng)用服務(wù)器及數(shù)據(jù)庫宕機(jī)、網(wǎng)絡(luò)交換機(jī)宕機(jī)、磁盤損壞、網(wǎng)卡松掉等等。還有各種軟件故障,程序Bug什么的。即使沒有Bug,程序要升級,必須要關(guān)閉進(jìn)程重新啟動,這段時間應(yīng)用也是不可用的;此外,還有外部環(huán)境引發(fā)的不可用,比如促銷引來大量用戶訪問,導(dǎo)致系統(tǒng)并發(fā)壓力太大而崩潰,以及,黑客攻擊、機(jī)房火災(zāi)、挖掘機(jī)挖斷光纜,各種情況導(dǎo)致的應(yīng)用不可用。
而互聯(lián)網(wǎng)的高可用是說,在上面各種情況下,應(yīng)用都要是可用的,用戶都能夠正常訪問系統(tǒng),完成業(yè)務(wù)處理。
這似乎是不可能的任務(wù)。
高可用的度量
首先我們看下,什么叫做應(yīng)用的高可用,以及可用性如何度量。業(yè)界通常用多少個9來說明互聯(lián)網(wǎng)應(yīng)用的可用性。比如說淘寶的可用性是4個9,就是說淘寶的服務(wù)99.99%可用。這句話的意思是,淘寶的服務(wù)要保證在所有的運(yùn)行時間里只有0.01%不可用,也就是說一年大概有53分鐘不可用。這個99.99%就叫做系統(tǒng)的可用性指標(biāo),這個值的計算公式是:
一般說來,兩個9表示系統(tǒng)基本可用,年度不可用時間小于88小時;3個9是較高可用,年度不可用時間小于9個小時;4個9是具有自動恢復(fù)能力的高可用,年度不可用時間小于53分鐘;5個9指極高的可用性,年度不可用時間小于5分鐘。我們熟悉的互聯(lián)網(wǎng)產(chǎn)品的可用性大多是4個9。淘寶、百度、微信,差不多都是這樣。
下面我會討論各種高可用技術(shù)方案。但不管是哪種方案,實現(xiàn)高可用需要投入的技術(shù)和設(shè)備成本都非常高。因此可用性并不是越高越好,而是要根據(jù)產(chǎn)品策略尋找高可用投入產(chǎn)出的最佳平衡點,像支付寶這樣的金融產(chǎn)品就需要更高的可用性,而微博的可用性要求就會相對低一些。
可用性指標(biāo)是對系統(tǒng)整體可用性的一個度量。在互聯(lián)網(wǎng)企業(yè)中,為了更好地管理系統(tǒng)的可用性,界定好系統(tǒng)故障以后的責(zé)任,通常會用故障分進(jìn)行管理。一般過程是,根據(jù)系統(tǒng)可用性指標(biāo)換算成一個故障分,這個故障分是整個系統(tǒng)的故障分,比如10萬分,然后根據(jù)各自團(tuán)隊各個產(chǎn)品各個職能角色承擔(dān)的責(zé)任的不同,把故障分下發(fā)給每個團(tuán)隊,直到每個人,也就是說每個工程師在年初的時候就會收到一個預(yù)計的故障分。然后每一次系統(tǒng)出現(xiàn)可用性故障的時候,都會進(jìn)行故障考核,劃定到具體的團(tuán)隊和責(zé)任人以后,會扣除他的故障分。如果到了年底的時候,如果一個工程師的故障分為負(fù)分,那么很有可能會影響他的績效考核。
圖片
高可用的架構(gòu)
系統(tǒng)的高可用架構(gòu)就是要在上述各種故障情況下,保證系統(tǒng)依然可以提供服務(wù),具體包含以下幾種架構(gòu)方案。我們已經(jīng)在前面幾篇架構(gòu)專欄中提到過這些架構(gòu)方案,這里我們從高可用的視角重新審視以下這些架構(gòu)是如何實現(xiàn)高可用的。
冗余備份
既然各種服務(wù)器故障是不可避免的,那么架構(gòu)設(shè)計上就要保證,當(dāng)服務(wù)器故障的時候,系統(tǒng)依然可以訪問。具體上就是要實現(xiàn)服務(wù)器的冗余備份。
冗余備份是說,提供同一服務(wù)的服務(wù)器要存在冗余,即任何服務(wù)都不能只有一臺服務(wù)器,服務(wù)器之間要互相進(jìn)行備份,任何一臺服務(wù)器出現(xiàn)故障的時候,請求可以發(fā)送到備份的服務(wù)器去處理。這樣,即使某臺服務(wù)器失效,在用戶看來,系統(tǒng)依然是可用的。
我在負(fù)載均衡架構(gòu)這篇文章中講了通過負(fù)載均衡服務(wù)器,將多臺應(yīng)用服務(wù)器構(gòu)成一個集群共同對外提供服務(wù),這樣可以利用多臺應(yīng)用服務(wù)器的計算資源,滿足高并發(fā)的用戶訪問請求。事實上,負(fù)載均衡還可以實現(xiàn)系統(tǒng)的高可用。
圖片
負(fù)載均衡服務(wù)器通過心跳檢測發(fā)現(xiàn)集群中某臺應(yīng)用服務(wù)器失效,然后負(fù)載均衡服務(wù)器就不將請求分發(fā)給這臺服務(wù)器,對用戶而言,也就感覺不到有服務(wù)器失效,系統(tǒng)依然可用。
回到我們開頭的問題,阿里巴巴就是用這種方法實現(xiàn)的。應(yīng)用程序升級的時候,停止應(yīng)用進(jìn)程,但是不影響用戶訪問。因為應(yīng)用程序部署在多臺服務(wù)器上,應(yīng)用程序升級的時候,每次只STOP一臺或者一部分服務(wù)器,在這些機(jī)器上進(jìn)行程序升級,這個時候,集群中還有其他服務(wù)器在提供服務(wù)器,因此用戶感覺不到服務(wù)器已經(jīng)停機(jī)了。
此外我在數(shù)據(jù)存儲架構(gòu)這篇文章中提到的數(shù)據(jù)庫主主復(fù)制,也是一種冗余備份。這個時候,不只是數(shù)據(jù)庫系統(tǒng)RDBMS互相進(jìn)行冗余備份,數(shù)據(jù)庫里的數(shù)據(jù)也要進(jìn)行冗余備份,一份數(shù)據(jù)存儲在多臺服務(wù)器里,保證當(dāng)任何一臺服務(wù)器失效,數(shù)據(jù)庫服務(wù)依然可以使用。
失敗隔離
保證系統(tǒng)高可用的另一個策略是失敗隔離,將失敗限制在一個較小的范圍之內(nèi),使故障影響范圍不擴(kuò)大。具體實現(xiàn)失敗隔離的主要架構(gòu)技術(shù)是消息隊列。
一方面,消息的生產(chǎn)者和消費者通過消息隊列進(jìn)行隔離。如果消費者出現(xiàn)故障的時候,生產(chǎn)者可以繼續(xù)向消息隊列發(fā)送消息,而不會感知到消費者的故障,等消費者恢復(fù)正常以后再去從消息隊列中消費消息,所以從用戶處理的視角看,系統(tǒng)一直是可用的。
發(fā)送郵件消費者出現(xiàn)故障,不會影響生產(chǎn)者應(yīng)用的運(yùn)行,也不會影響發(fā)送短信等其他消費者正常的運(yùn)行。
圖片
另一方面,由于分布式消息隊列具有削峰填谷的作用,所以在高并發(fā)的時候,消息的生產(chǎn)者可以將消息緩沖在分布式消息隊列中,消費者可以慢慢地從消息隊列中去處理,而不會將瞬時的高并發(fā)負(fù)載壓力直接施加到整個系統(tǒng)上,導(dǎo)致系統(tǒng)崩潰。也就是將壓力隔離開來,使消息生產(chǎn)者的訪問壓力不會直接傳遞到消息的消費者,這樣可以提高數(shù)據(jù)庫等對壓力比較敏感的服務(wù)的可用性。
同時,消息隊列還使得程序解耦,將程序的調(diào)用和依賴隔離開來,我們知道,低耦合的程序更加易于維護(hù),也可以減少程序出現(xiàn)Bug的幾率。
限流降級
限流和降級也是保護(hù)系統(tǒng)高可用的一種手段。在高并發(fā)場景下,如果系統(tǒng)的訪問量超過了系統(tǒng)的承受能力,可以通過限流對系統(tǒng)進(jìn)行保護(hù)。限流是指對進(jìn)入系統(tǒng)的用戶請求進(jìn)行流量限制,如果訪問量超過了系統(tǒng)的最大處理能力,就會丟棄一部分的用戶請求,保證整個系統(tǒng)可用,保證大部分用戶是可以訪問系統(tǒng)的。這樣雖然有一部分用戶的請求被丟棄,產(chǎn)生了部分不可用,但還是好過整個系統(tǒng)崩潰,所有的用戶都不可用要好。
降級是保護(hù)系統(tǒng)的另一種手段。有一些系統(tǒng)功能是非核心的,但是它也給系統(tǒng)產(chǎn)生了非常大的壓力,比如說在電商系統(tǒng)中有確認(rèn)收貨這個功能,即便我們不去確認(rèn)收貨,系統(tǒng)也會超時自動確認(rèn)收貨。
但實際上確認(rèn)收貨這個操作是一個非常重的操作,因為它會對數(shù)據(jù)庫產(chǎn)生很大的壓力:它要進(jìn)行更改訂單狀態(tài),完成支付確認(rèn),并進(jìn)行評價等一系列操作。如果在系統(tǒng)高并發(fā)的時候去完成這些操作,那么會對系統(tǒng)雪上加霜,使系統(tǒng)的處理能力更加惡化。
解決辦法就是在系統(tǒng)高并發(fā)的時候,比如說像淘寶雙11的時候,當(dāng)天可能整天系統(tǒng)都處于一種極限的高并發(fā)訪問壓力之下,這時候就可以將確認(rèn)收貨、評價這些非核心的功能關(guān)閉,將寶貴的系統(tǒng)資源留下來,給正在購物的人,讓他們?nèi)ネ瓿山灰住?/p>
異地多活
我們前面提到的各種高可用策略,都還是針對一個數(shù)據(jù)中心內(nèi)的系統(tǒng)架構(gòu),針對服務(wù)器級別的軟硬件故障而言的。但如果整個數(shù)據(jù)中心都不可用,比如說數(shù)據(jù)中心所在城市遭遇了地震,機(jī)房遭遇了火災(zāi)或者停電,這樣的話,不管我們前面的設(shè)計和系統(tǒng)多么的高可用,系統(tǒng)依然是不可用的。
為了解決這個問題,同時也為了提高系統(tǒng)的處理能力和改善用戶體驗,很多大型互聯(lián)網(wǎng)應(yīng)用都采用了異地多活的多機(jī)房架構(gòu)策略,也就是說將數(shù)據(jù)中心分布在多個不同地點的機(jī)房里,這些機(jī)房都可以對外提供服務(wù),用戶可以連接任何一個機(jī)房進(jìn)行訪問,這樣每個機(jī)房都可以提供完整的系統(tǒng)服務(wù),即使某一個機(jī)房不可使用,系統(tǒng)也不會宕機(jī),依然保持可用。
異地多活的架構(gòu)考慮的重點就是,用戶請求如何分發(fā)到不同的機(jī)房去。這個主要可以在域名解析的時候完成,也就是用戶進(jìn)行域名解析的時候,會根據(jù)就近原則或者其他一些策略,完成用戶請求的分發(fā)。另一個至關(guān)重要的技術(shù)點是,因為是多個機(jī)房都可以獨立對外提供服務(wù),所以也就意味著每個機(jī)房都要有完整的數(shù)據(jù)記錄。用戶在任何一個機(jī)房完成的數(shù)據(jù)操作,都必須同步傳輸給其他的機(jī)房,進(jìn)行數(shù)據(jù)實時同步。
數(shù)據(jù)庫實時同步最需要關(guān)注的就是數(shù)據(jù)沖突問題。同一條數(shù)據(jù),同時在兩個數(shù)據(jù)中心被修改了,該如何解決?為了解決這種數(shù)據(jù)沖突的問題,某些容易引起數(shù)據(jù)沖突的服務(wù)采用類似MySQL的主主模式,也就是說多個機(jī)房在某個時刻是有一個主機(jī)房的,某些請求只能到達(dá)主機(jī)房才能被處理,其他的機(jī)房不處理這一類請求,以此來避免關(guān)鍵數(shù)據(jù)的沖突。
小結(jié)
除了以上的高可用架構(gòu)方案,還有一些高可用的運(yùn)維方案:通過自動化測試減少系統(tǒng)的Bug;通過自動化監(jiān)控盡早發(fā)現(xiàn)系統(tǒng)的故障;通過預(yù)發(fā)布驗證發(fā)現(xiàn)測試環(huán)境無法發(fā)現(xiàn)的Bug;灰度發(fā)布降低軟件錯誤帶來的影響以及評估軟件版本升級帶來的業(yè)務(wù)影響等等。