現(xiàn)在而今眼目下,互聯(lián)網(wǎng)越來越發(fā)達(dá),大家不管是幾乎每天24小時不離手的手機,還是工作時每天要打開的電腦,還是我們使用的各種社會工具和衣食住行的方方面面,各種軟件應(yīng)用已經(jīng)成為我們生活中必不可少的一部分。軟件的功能是否能滿足需求,是我們關(guān)心的首要問題,軟件是否穩(wěn)定,也是一個重要的指標(biāo),也就是軟件系統(tǒng)的可用性如何,在遇到一些問題時,系統(tǒng)的容錯性如何,是否仍然能夠?qū)ν馓峁┓?wù),以及服務(wù)的效率如何,是迅速就能返回還是要等待很久。
回顧剛過去的2019年一年,全球大型互聯(lián)網(wǎng)公司發(fā)生了數(shù)十起宕機事故,google、Microsoft、Amazon 、Facebook、阿里、騰訊等無一幸免,短則數(shù)分鐘,長則數(shù)小時,有的是天災(zāi),有的是人禍。對于用戶上億的商業(yè)應(yīng)用來說,哪怕是一分鐘的宕機,損失都非常巨大,一個中等嚴(yán)重的故障可能會讓你損失一輛車,一個outage可能就會讓你損失數(shù)座房子了。騰訊2018年宕機4小時,據(jù)估算損失了5000多萬。

如何衡量軟件的可用性呢?
其度量方式,是根據(jù)系統(tǒng)損害、無法使用的時間,以及由無法運作恢復(fù)到可運作狀況的時間,與系統(tǒng)總運作時間的比較。計算公式為:
A=MTBF/(MTBF+MDT)
A(可用性),MTBF(平均故障間隔),MDT(平均修復(fù)時間)
可用性為99.999%的系統(tǒng),一年故障時間為5分15秒;可用性為99.99%的系統(tǒng),一年故障時間為5分34秒;可用性為99.9%的系統(tǒng),一年故障時間為8小時46分。
在線系統(tǒng)和執(zhí)行關(guān)鍵任務(wù)的系統(tǒng)通常要求其可用性要達(dá)到5個9標(biāo)準(zhǔn)(99.999%)。
當(dāng)然,除了這個數(shù)據(jù)之外,我們也要看宕機發(fā)生在什么時候,才能綜合評估事故影響的嚴(yán)重性,造成的損失到底有多大。當(dāng)我們不得不主動的關(guān)閉或重啟一些服務(wù)的時候,如何把影響減到最小。
軟件應(yīng)用一旦進(jìn)入生產(chǎn)系統(tǒng),接入了大量的用戶,我們就得盡量保證它是24小時可用的,為用戶提供不間斷的服務(wù)。軟件的高可用性(High availability -- HA),是一項非功能特性,在大型產(chǎn)品的架構(gòu)設(shè)計階段就需要專門納入進(jìn)來考慮。軟件系統(tǒng)會劃分為多個不同的模塊,重要的組件或模塊,都需要列出HA的Feature實施計劃。
為了實現(xiàn)軟件系統(tǒng)的高可用性,都有哪些關(guān)注點和技術(shù)手段呢?
1. 冗余設(shè)計和Fail over
冗余設(shè)計的核心目的是為了避免單點故障導(dǎo)致整個系統(tǒng)不可用,在不同的層級增加冗余,當(dāng)單點故障發(fā)生時,可動態(tài)的把服務(wù)切換到正常的上面,從而能夠繼續(xù)為用戶提供服務(wù)。冗余設(shè)計可以從幾個方面進(jìn)行考量:
1. 地理環(huán)境,如果你的所有服務(wù)器都在同一個地方,一場洪水或地震將讓你的整個系統(tǒng)和數(shù)據(jù)化為烏有。大型系統(tǒng)通常都會把服務(wù)器或數(shù)據(jù)中心分布在不同的地域。這將有助于提高系統(tǒng)的可用性。
2.硬件,高可用的服務(wù)器必須對諸如停電,硬件損壞(如硬盤損壞,網(wǎng)卡損壞)等具有承受能力。
3. 軟件,從操作系統(tǒng)到應(yīng)用程序本身的整個軟件棧,都需要設(shè)計來能應(yīng)對非預(yù)期的失敗而必須重啟的情況。
4.數(shù)據(jù),由于硬盤故障帶來的數(shù)據(jù)丟失和數(shù)據(jù)不一致的情況,如何處理。高可用性系統(tǒng)必須要把故障情況下的數(shù)據(jù)安全考慮進(jìn)來。
5.網(wǎng)絡(luò),非預(yù)期的網(wǎng)絡(luò)中斷是系統(tǒng)可能遇到的另一個問題。網(wǎng)絡(luò)冗余策略也是非常有價值的。
先來看看軟件設(shè)計層面應(yīng)當(dāng)如何考慮冗余,一個比較典型的B/S 架構(gòu)的應(yīng)用如下圖所示。

我們在其每一層,都可以做冗余處理。
在網(wǎng)關(guān)層,我們可以配置多個網(wǎng)關(guān),用keepalived管理起來,給它們分配一個浮動IP,當(dāng)主網(wǎng)關(guān)出問題時,我們可以把浮動IP分配給備用網(wǎng)關(guān),這樣仍然可以繼續(xù)對外提供服務(wù)。
在Web應(yīng)用層,可以配置多個web后端,比如使用Nginx做網(wǎng)關(guān)層的反向代理時,它可以配置多個web后端,并且能夠檢測到多個后端的存活性,當(dāng)一個web服務(wù)掛了時,nginx可以自動進(jìn)行故障轉(zhuǎn)移,將流量遷移到其他的web服務(wù)上。
業(yè)務(wù)服務(wù)層也可以實現(xiàn)冗余,在web應(yīng)用層通過建立服務(wù)連接池來與下游的服務(wù)建立多個連接,每次請求可以隨機的選擇連接來訪問下層的服務(wù)。當(dāng)某一個service掛了的時候,連接池的管理器能夠檢測到,并自動進(jìn)行故障轉(zhuǎn)移。
大型系統(tǒng)的服務(wù)層下通常會有一個緩存層,數(shù)據(jù)緩存也可以實現(xiàn)冗余來達(dá)到高可用,
我們在每一個邏輯層,都可以增加冗余。如果其中一個服務(wù)掛了,可以通過自動的故障轉(zhuǎn)移(fail over),讓其他服務(wù)點繼續(xù)提供服務(wù)。通過緩存數(shù)據(jù)的冗余實現(xiàn)的,常見實踐是緩存客戶端雙讀雙寫,或者利用緩存集群的主從數(shù)據(jù)同步與sentinel保活與自動故障轉(zhuǎn)移;更多的業(yè)務(wù)場景,對緩存沒有高可用要求,可以使用緩存服務(wù)化來對調(diào)用方屏蔽底層復(fù)雜性
數(shù)據(jù)庫層的冗余可以采用”主從同步,讀寫分離”的架構(gòu),可以把它分為“讀庫高可用”和“寫庫高可用”兩類。“讀庫高可用”的常見實踐是通過db-connection-pool來保證自動故障轉(zhuǎn)移,“寫庫高可用”則是通過寫庫的冗余實現(xiàn)的,常見實踐是keepalived + virtual IP自動故障轉(zhuǎn)移。
文件存儲部分,對于上層文件系統(tǒng)而言,邏輯上是一個整體,我們可以使用分布式文件系統(tǒng),利用分布式集群,提供對文件系統(tǒng)的支持,對外提供統(tǒng)一的命名空間,文件系統(tǒng)內(nèi)部也實現(xiàn)了各種冗余和負(fù)載均衡操作,有比較好的容錯性,提高了文件系統(tǒng)的可用性。
分布式文件系統(tǒng)廣泛流行前,對存盤數(shù)據(jù)的高可靠性,可以引入RAID(獨立冗余磁盤陣列)。RAID 技術(shù)將多個單獨的物理硬盤以不同的方式組合成一個邏輯硬盤,同一份數(shù)據(jù)會以一定組合方式,寫入多塊磁盤。即使其中部分磁盤損壞,仍然可以保證數(shù)據(jù)的可用性。
2. 無狀態(tài)設(shè)計
要想服務(wù)能夠切換,做故障轉(zhuǎn)移(fail over或fail back),很重要的一點就是不要保存業(yè)務(wù)的上下文信息,而僅根據(jù)每次請求提交的數(shù)據(jù)進(jìn)行相應(yīng)的邏輯處理,這樣的話,多個服務(wù)實例之間就是完全對等的,請求提交到任意服務(wù)器,處理的結(jié)果是完全一樣的。在這個情況下,我們就可以利用負(fù)載均衡進(jìn)行無狀態(tài)服務(wù)的失效轉(zhuǎn)移。
但是我們總要管理session信息,這些信息可以下沉到緩存層和數(shù)據(jù)庫層。這樣服務(wù)層就可以根據(jù)需要做到動態(tài)的Scale-out/Scale-In和高可用了。

3. 負(fù)載均衡
上文談到的幾點,都可以看到負(fù)載均衡的影子,大型系統(tǒng)的網(wǎng)關(guān)層基本都會引入負(fù)載均衡,負(fù)載均衡可以提高系統(tǒng)并發(fā)性支持,反過來說減輕了單一服務(wù)的壓力,提高了系統(tǒng)的可用性,通常會有兩種形式:
· 硬件負(fù)載—F5 7層或4層網(wǎng)絡(luò)代理
· 軟件負(fù)載—Nginx, Haproxy等開源的負(fù)載均衡軟件。
常用的算法通常有
1. 輪詢法
2. 隨機法
3. 源地址哈希法
4. 加權(quán)輪詢法
5. 加權(quán)隨機法
6. 最小連接數(shù)法

4.冪等設(shè)計
什么是冪等性呢,簡而言之就是同樣的請求,即使多次重復(fù),也必須得到相同的結(jié)果。
冪等性在支付類場景尤為重要,比如重復(fù)的的訂單號,同樣的金額,不做冪等性設(shè)計,重復(fù)支付可能就會造成金額累加。
對于服務(wù)冪等性設(shè)計的要點就是一定要效驗請求參數(shù)有效性,及已有數(shù)據(jù)的對比。如果同樣的請求參數(shù)已經(jīng)處理過就不要重復(fù)處理,直接返回,這就是冪等性核心點。
5. 超時機制
我們的服務(wù),尤其是微服務(wù)化的場景下,通過REST API進(jìn)行相互調(diào)用,為了保證系統(tǒng)的可用性,就需要對調(diào)用設(shè)置超時機制,一旦被調(diào)用服務(wù)超時還未返回,主調(diào)服務(wù)就應(yīng)該進(jìn)入超時處理流程。這樣就不至于在某個服務(wù)不可用時,整個系統(tǒng)進(jìn)入阻塞狀態(tài)。
6.異步調(diào)用
采用異步調(diào)用方式調(diào)用被調(diào)用的服務(wù),有助于將主調(diào)服務(wù)和被調(diào)服務(wù)解耦,減少等待時被阻塞的情況,并能提高系統(tǒng)的并發(fā)性能。對于不需要關(guān)心直接返回結(jié)果的請求類型以及當(dāng)請求流程不是系統(tǒng)的關(guān)鍵路徑時,采用異步化是非常有價值的。
7.服務(wù)分級與降級
在一個大系統(tǒng)中,一般會有核心服務(wù)和次要服務(wù)之分,那么對于不同的服務(wù)可以采用不同的處理方案,出現(xiàn)故障時應(yīng)該優(yōu)先保證核心服務(wù)的運行,對于次要的服務(wù),可以延遲服務(wù)或在粒度范圍內(nèi)關(guān)閉服務(wù)。服務(wù)降級一般是關(guān)注業(yè)務(wù),對業(yè)務(wù)進(jìn)行考慮,抓住業(yè)務(wù)的層級,從而決定在哪一層上進(jìn)行處理:比如在IO層,業(yè)務(wù)邏輯層,還是在外圍進(jìn)行處理。
8.服務(wù)熔斷
服務(wù)熔斷也叫服務(wù)的過載保護,服務(wù)熔斷對服務(wù)提供了proxy,防止服務(wù)不可能時,出現(xiàn)串聯(lián)故障(cascading failure),導(dǎo)致雪崩效應(yīng)。服務(wù)熔斷一般是某個服務(wù)(下游服務(wù))故障引起,而服務(wù)降級一般是從整體負(fù)荷考慮;對于設(shè)計的任何一個系統(tǒng),都需要進(jìn)行容量的預(yù)估和最大容量設(shè)置,當(dāng)外部請求量超過最大QPS容量時,應(yīng)該啟動防雪崩機制,以避免大量外部請求把服務(wù)壓跨而不能對外提供服務(wù)。
9.系統(tǒng)監(jiān)控
大型系統(tǒng)的服務(wù)模塊眾多,經(jīng)常會因為各種原因出現(xiàn)進(jìn)程掛掉,網(wǎng)絡(luò)質(zhì)量不好,機器宕機等現(xiàn)象,我們設(shè)計的系統(tǒng)應(yīng)該具備監(jiān)控上報和告警的能力,運維和開發(fā)能夠通過監(jiān)控報表實時的查看系統(tǒng)的運行狀態(tài)。服務(wù)一旦出現(xiàn)問題能夠及時發(fā)現(xiàn),通過自動化處理,或者人工介入處理,從而達(dá)到縮短系統(tǒng)的不可用時間,提高可用性。
常見的監(jiān)控指標(biāo)有:CPU、帶寬、內(nèi)存使用率、網(wǎng)絡(luò)連接狀態(tài),系統(tǒng)調(diào)用錯誤,成功率,PV,UV等。
談了主要的應(yīng)用治理,解析來要說說數(shù)據(jù)治理,它對于提高系統(tǒng)的響應(yīng)效率也非常重要。
10.數(shù)據(jù)緩存
當(dāng)讀寫的并發(fā)數(shù)越來越大時,數(shù)據(jù)庫很容易成為系統(tǒng)的瓶頸,因此在大型系統(tǒng)中,緩存系統(tǒng)的設(shè)計就非常有必要了,設(shè)計良好的緩存系統(tǒng),可以大大提高系統(tǒng)的可用性和并發(fā)效率。

11.數(shù)據(jù)庫優(yōu)化
我們需要的數(shù)據(jù)庫不僅是要穩(wěn)定,能提供服務(wù),也要考慮其提供服務(wù)的效率,當(dāng)數(shù)據(jù)庫里面的數(shù)據(jù)有百萬條以上時,查詢效率就變得很低了。不僅需要考慮前面提及的讀寫分離,我們也需要根據(jù)業(yè)務(wù)特點,做出適當(dāng)?shù)姆謳臁⒎直怼⒎謪^(qū)策略,建立合適的索引。從而大大提高數(shù)據(jù)插入和查詢效率。
前面講了架構(gòu)設(shè)計時應(yīng)當(dāng)考慮的高可用的技術(shù)手段,還有一點不可忽視的是我們的產(chǎn)品如何部署到生產(chǎn)系統(tǒng)中,尤其是在做產(chǎn)品升級的時候,升級是offline的還是online的?我們能保證不帶來一點down time嗎?如果系統(tǒng)部署到一半失敗了,而線上數(shù)據(jù)還在源源不斷的進(jìn)來,怎么處理?如果部署失敗了,系統(tǒng)能在不丟失新數(shù)據(jù)的情況下回滾嗎?這些都可用性設(shè)計時需要考慮的問題,做不好就會帶來糟糕的客戶體驗。