工作十年,談?wù)勎业母呖捎眉軜?gòu)和系統(tǒng)設(shè)計(jì)經(jīng)驗(yàn)
一、高可用架構(gòu)和系統(tǒng)設(shè)計(jì)思想
可用性和高可用概念
可用性是一個(gè)可以量化的指標(biāo),計(jì)算的公式在維基百科中是這樣描述的:根據(jù)系統(tǒng)損害、無法使用的時(shí)間,以及由無法運(yùn)作恢復(fù)到可運(yùn)作狀況的時(shí)間,與系統(tǒng)總運(yùn)作時(shí)間的比較。行業(yè)內(nèi)一般用幾個(gè)9表示可用性指標(biāo),對應(yīng)用的可用性程度一般衡量標(biāo)準(zhǔn)有三個(gè)9到五個(gè)9;一般我們的系統(tǒng)至少要到 4 個(gè) 9(99.99%)的可用性才能談得上高可用。
高可用(High Availability)的定義:(From 維基百科)是 IT 術(shù)語,指系統(tǒng)無中斷地執(zhí)行其功能的能力,代表系統(tǒng)的可用性程度,是進(jìn)行系統(tǒng)設(shè)計(jì)時(shí)的準(zhǔn)則之一。服務(wù)不可能 100% 可用,因此要提高我們的高可用設(shè)計(jì),就要盡最大可能的去增加我們服務(wù)的可用性,提高可用性指標(biāo)。一句話來表述就是:高可用就是讓我們的服務(wù)在任何情況下都盡最大可能能夠?qū)ν馓峁┓?wù)。
高可用系統(tǒng)設(shè)計(jì)思想
高可用系統(tǒng)的設(shè)計(jì),需要有一套比較科學(xué)的工程管理套路,要從產(chǎn)品、開發(fā)、運(yùn)維、基建等全方位去考量和設(shè)計(jì),高可用系統(tǒng)的設(shè)計(jì)思想包括但不限于:
- 做好研發(fā)規(guī)范,系統(tǒng)都是研發(fā)人員設(shè)計(jì)和編碼寫出來的,因此首先要對研發(fā)層面有一個(gè)規(guī)范和標(biāo)準(zhǔn)
- 做好容量規(guī)劃和評估,主要是讓開發(fā)人員對系統(tǒng)要抗住的量級有一個(gè)基本認(rèn)知,方便進(jìn)行合理的架構(gòu)設(shè)計(jì)和演進(jìn)。
- 做好服務(wù)層面的高可用,主要是負(fù)載均衡、彈性擴(kuò)縮容、異步解耦、故障容錯(cuò)、過載保護(hù)等。
- 做好存儲(chǔ)層面的高可用,主要是冗余備份(熱備、冷備)、失效轉(zhuǎn)移(確認(rèn),轉(zhuǎn)移,恢復(fù))等。
- 做好運(yùn)維層面的高可用,主要是發(fā)布測試、監(jiān)控告警、容災(zāi)、故障演練等。
- 做好產(chǎn)品層面的高可用,主要是兜底策略。
- 做好應(yīng)急預(yù)案,主要是在出現(xiàn)問題后怎么快速恢復(fù),不至于讓我們的異常事態(tài)擴(kuò)大。
二、研發(fā)規(guī)范層面
方案設(shè)計(jì)和編碼規(guī)范
研發(fā)規(guī)范層面這個(gè)是大家容易忽視的一個(gè)點(diǎn),但是,我們所有的設(shè)計(jì),都是研發(fā)人員來完成的,包括從設(shè)計(jì)文檔到編碼到發(fā)布上線,因此,研發(fā)層面也是有一個(gè)規(guī)范流程和套路,來讓我們更好的去研發(fā)和維護(hù)一個(gè)高可用的系統(tǒng):
- 設(shè)計(jì)階段
規(guī)范好相關(guān)方案設(shè)計(jì)文檔的模板和提綱,讓團(tuán)隊(duì)內(nèi)部保持統(tǒng)一,可以參考我的文章《??技術(shù)方案設(shè)計(jì)模板??》
方案設(shè)計(jì)后一定要進(jìn)行評審,在我們團(tuán)隊(duì)中,新項(xiàng)目一定要評審,重構(gòu)項(xiàng)目一定要評審,大的系統(tǒng)優(yōu)化或者升級一定要評審,其他的一般研發(fā)工作量超過一周的建議要評審的。
- 編碼階段
不要隨便打日志
要接入遠(yuǎn)程日志
要能夠分布式鏈路追蹤
代碼編寫完需要有一定的單測來保證代碼的健壯性,同時(shí)也能保障我們后續(xù)調(diào)整邏輯或者優(yōu)化的時(shí)候可以保證代碼的穩(wěn)定
包括增量覆蓋率、全量覆蓋率,具體的覆蓋率要達(dá)到多少可以根據(jù)團(tuán)隊(duì)內(nèi)部的實(shí)際情況來定,在我們團(tuán)隊(duì),定的規(guī)則是 50% 的覆蓋率。
工程的 layout 目錄結(jié)構(gòu)規(guī)范,團(tuán)隊(duì)內(nèi)部保持統(tǒng)一,盡量簡潔
遵循團(tuán)隊(duì)內(nèi)部的代碼規(guī)范,一般公司都有對應(yīng)語言的規(guī)范,如果沒有則參考官方的規(guī)范,代碼規(guī)范可以大大減少 bug 并且提高可用性。
執(zhí)行代碼規(guī)范
單測覆蓋率
日志規(guī)范
- 發(fā)布上線階段,參考下面運(yùn)維部署層面那一章節(jié)的灰度發(fā)布和接口測試相關(guān)說明
容量規(guī)劃和評估
容量評估,是指我們需要評估好,我們這個(gè)系統(tǒng),是為了應(yīng)對一個(gè)什么體量的業(yè)務(wù),這個(gè)業(yè)務(wù)請求量的平均值、高峰的峰值大概都在一個(gè)什么級別。如果是新系統(tǒng),那么就需要根據(jù)產(chǎn)品和運(yùn)營同學(xué)對業(yè)務(wù)有一個(gè)大體的預(yù)估,然后開發(fā)同學(xué)根據(jù)產(chǎn)品給的數(shù)據(jù)再進(jìn)行詳細(xì)的評估。如果是老系統(tǒng),那么就可以根據(jù)歷史數(shù)據(jù)來評估。評估的時(shí)候,要從一個(gè)整體角度來看全局的量級,然后再細(xì)化到每個(gè)子業(yè)務(wù)模塊要承載的量級。
容量規(guī)劃,是指我們系統(tǒng)在設(shè)計(jì)的時(shí)候,就要能夠初步規(guī)劃好我們的系統(tǒng)大致能夠抗多少的量級,比如是十萬還是百萬級別的請求量,或者更多。不同的量級對應(yīng)的系統(tǒng)架構(gòu)的設(shè)計(jì)會(huì)完全不一樣,尤其到了千萬、億級別的量級的時(shí)候,架構(gòu)的設(shè)計(jì)會(huì)有很多的考量。當(dāng)然這里需要注意的是,我們不需要一上來就設(shè)計(jì)出遠(yuǎn)超于我們當(dāng)前業(yè)務(wù)真實(shí)流量的系統(tǒng),要根據(jù)業(yè)務(wù)實(shí)際情況來設(shè)計(jì)。同時(shí),容量規(guī)劃還涉及到,我們系統(tǒng)上下游的各個(gè)模塊、依賴的存儲(chǔ)、依賴的三方服務(wù),分別需要多少資源,需要有一個(gè)相對可以量化的數(shù)據(jù)出來。容量規(guī)劃階段,更多是要依靠自身和團(tuán)隊(duì)的經(jīng)驗(yàn),比如要了解我們的 log 的性能、redis 的性能、rpc 接口的性能、服務(wù)化框架的性能等等,然后根據(jù)各種組件的性能來綜合評估自己設(shè)計(jì)的系統(tǒng)的整體性能情況。
容量評估和容量規(guī)劃之后,我們還需要做一件事情,就是性能壓測,最好是能夠做到全鏈路壓測。性能壓測的目的是為了確保你的容量規(guī)劃是準(zhǔn)確的,比如我設(shè)計(jì)的這個(gè)系統(tǒng),我規(guī)劃的是能夠抗千萬級別的請求,那么實(shí)際上,真的能夠抗住嗎 ?這個(gè)在上線之前,首先要根據(jù)經(jīng)驗(yàn)來判斷,然后是一定要經(jīng)過性能壓測得出準(zhǔn)確結(jié)論的。性能壓測要關(guān)注的指標(biāo)很多,但是重點(diǎn)要關(guān)注是兩個(gè)指標(biāo),一個(gè)是 QPS、一個(gè)是響應(yīng)耗時(shí),要確保壓測的結(jié)果符合預(yù)期。壓測的步驟可以先分模塊單獨(dú)壓測,最后如果情況允許,那么最好執(zhí)行全鏈路壓測。
QPS 預(yù)估(漏斗型)
QPS 預(yù)估(漏斗型),指的是一個(gè)真實(shí)的請求過來后,從接入層開始,分別經(jīng)過了我們整個(gè)系統(tǒng)的哪些層級、哪些模塊,然后每一個(gè)層級的 QPS 的量級分別有多少,從請求鏈路上來看,層級越往下,那么下游層級的量級應(yīng)該會(huì)逐步減少的,因?yàn)槊拷?jīng)過一個(gè)層級,都有可能會(huì)被各種條件過濾掉的一部分請求。比如說進(jìn)入活動(dòng)頁后查看商品詳情然后下單這個(gè)例子,首先進(jìn)入活動(dòng)頁,所有的請求都會(huì)進(jìn)入訪問;然后只會(huì)有部分用戶查詢商品詳情;最后查看商品詳情的這些用戶又只會(huì)有部分用戶會(huì)下單,因此這里就會(huì)有一個(gè)漏斗,從上層模塊到下層模塊的量級一定是逐步減少的。
QPS 預(yù)估(漏斗型)就是需要我們按照請求的層面和模塊來構(gòu)建我們的預(yù)估漏斗模型,然后預(yù)估好每一個(gè)層級的量級,包括但不限于從服務(wù)、接口、分布式緩存等各個(gè)層面來預(yù)估,最后構(gòu)成我們完整的 QPS 漏斗模型。
三、應(yīng)用服務(wù)層面
無狀態(tài)和負(fù)載均衡設(shè)計(jì)
一般要做到系統(tǒng)的高可用,我們的應(yīng)用服務(wù)的常規(guī)設(shè)計(jì)都是無狀態(tài)的,這也就意味著,我們可以部署多個(gè)實(shí)例來提高我們系統(tǒng)的可用性,而這多個(gè)實(shí)例之間的流量分配,就需要依賴我們的負(fù)載均衡能力。無狀態(tài) + 負(fù)載均衡 既可以讓我們的系統(tǒng)提高并發(fā)能力,也可以提高我們系統(tǒng)的可用性。
如果我們的業(yè)務(wù)服務(wù)使用的是各種微服務(wù)框架來開發(fā)的,那么大概率在這個(gè)微服務(wù)框架里面就會(huì)包含了服務(wù)發(fā)現(xiàn)和負(fù)載均衡的能力。這是一整套流程,包括服務(wù)注冊和發(fā)現(xiàn)、負(fù)載均衡、健康狀態(tài)檢查和自動(dòng)剔除。當(dāng)我們的任何一個(gè)服務(wù)實(shí)例出現(xiàn)故障后會(huì)被自動(dòng)剔除掉,當(dāng)我們有新增一個(gè)服務(wù)實(shí)例后會(huì)自動(dòng)添加進(jìn)來提供服務(wù)。
如果我們不是使用的微服務(wù)框架來開發(fā)的,那么就需要依賴負(fù)載均衡的代理服務(wù),比如 LVS、Nginx 來幫我們實(shí)現(xiàn)負(fù)載均衡。
彈性擴(kuò)縮容設(shè)計(jì)
彈性擴(kuò)縮容設(shè)計(jì)是應(yīng)對突峰流量的非常有效的手段之一,同時(shí)也是保障我們服務(wù)可用性的必要手段。彈性擴(kuò)縮容針對的是我們的無狀態(tài)的應(yīng)用服務(wù)而言的,因?yàn)榉?wù)是無狀態(tài)的,因此可以隨時(shí)根據(jù)請求量的大小來進(jìn)行擴(kuò)縮容,流量大就擴(kuò)容來應(yīng)對大量請求,流量小的時(shí)候就縮容減少資源占用。
怎么實(shí)現(xiàn)彈性擴(kuò)縮容呢?現(xiàn)階段都是云原生時(shí)代,大部分的公司都是采用容器化(K8s)部署,那么基于這個(gè)情況的話,彈性擴(kuò)縮容就非常容易了,只需要配置好 K8s 的彈性條件就能自動(dòng)根據(jù) CPU 的使用率來實(shí)現(xiàn)。
如果不是容器化部署,是物理機(jī)部署的方式,那么要做到彈性擴(kuò)縮容,必須要有一個(gè)公司內(nèi)部的基礎(chǔ)建設(shè)能力,能夠在運(yùn)營平臺(tái)上針對服務(wù)的 CPU 或者 QPS 進(jìn)行監(jiān)控,如果超過一定的比例就自動(dòng)擴(kuò)縮容,和 K8s 的彈性原理是一樣的,只是需要自行實(shí)現(xiàn)。
異步解耦和削峰設(shè)計(jì)(消息隊(duì)列)
要想我們的系統(tǒng)能夠高可用,那么從架構(gòu)層面來說,要做到分層、分模塊來設(shè)計(jì),而分層分模塊之后,那么各個(gè)模塊之間,還可以進(jìn)行異步處理、解耦處理。目的是為了不相互影響,通過異步和解耦可以使我們的架構(gòu)大大的提升可用性。
架構(gòu)層面的異步解耦的方式就是采用消息隊(duì)列(比如常見的 Kafka),并且同時(shí)消息隊(duì)列還有削峰的作用,這兩者都可以提高我們的架構(gòu)可用性:
- 異步解耦:采用消息隊(duì)列之后,可以把同步的流程轉(zhuǎn)換為異步的流程,消息生成者和消費(fèi)者都只需要和消息隊(duì)列進(jìn)行交互,這樣不僅做了異步處理,還講消息生成者和消費(fèi)者進(jìn)行了隔離。異步處理的優(yōu)勢在于,不管消息的后續(xù)處理的業(yè)務(wù)服務(wù)是否 ok,只要消息隊(duì)列還沒滿,那么就可以執(zhí)行對外提供服務(wù),而消費(fèi)方則可以根據(jù)自身處理能力來消費(fèi)消息后進(jìn)行處理。解耦的優(yōu)勢在于,如果消費(fèi)方異常,那么并不影響生產(chǎn)方,依然可以對外提供服務(wù),消息消費(fèi)者恢復(fù)后可以繼續(xù)從消息隊(duì)列里面消費(fèi)數(shù)據(jù)后執(zhí)行業(yè)務(wù)邏輯
- 削峰:采用消息隊(duì)列之后,還可以做到削峰的作用,當(dāng)并發(fā)較高的時(shí)候,甚至是流量突發(fā)的時(shí)候,只要消息生產(chǎn)者能夠?qū)⑾懭氲较㈥?duì)列中,那么這個(gè)消息就不會(huì)丟,后續(xù)處理邏輯可以慢慢的去消息隊(duì)列里面消費(fèi)這些突發(fā)的流量數(shù)據(jù)。這樣就不會(huì)因?yàn)橛型话l(fā)流量而把整個(gè)系統(tǒng)打垮。
故障和容錯(cuò)設(shè)計(jì)
任何服務(wù),一定會(huì)存在失敗的情況,不可能有 100% 的可用,服務(wù)在線上運(yùn)行過程中,總會(huì)遇到各種各樣意想不到的問題會(huì)讓你的服務(wù)出現(xiàn)狀況,因此業(yè)界來評價(jià)可用性 SLA 都是說多少個(gè) 9,比如 4 個(gè) 9(99.99%)的可用性。
為此,我們的設(shè)計(jì)建議遵循"design for failure"的設(shè)計(jì)原則,設(shè)計(jì)出一套可容錯(cuò)的系統(tǒng),需要做到盡早返回、自動(dòng)修復(fù),細(xì)節(jié)如下
- 遵循 fail fast 原則,F(xiàn)ail fast 原則是說,當(dāng)我們的主流程的任何一步出現(xiàn)問題的時(shí)候,應(yīng)該快速合理地結(jié)束整個(gè)流程,盡快返回錯(cuò)誤,而不是等到出現(xiàn)負(fù)面影響才處理。
- 具備自我保護(hù)的能力。當(dāng)我們依賴的其他服務(wù)出現(xiàn)問題的時(shí)候,要盡快的進(jìn)行降級、兜底等各種異常保護(hù)措施,要避免出現(xiàn)連鎖反應(yīng)導(dǎo)致整個(gè)服務(wù)完全不可用。比如當(dāng)我們依賴的數(shù)據(jù)存儲(chǔ)出現(xiàn)問題,我們不能一直重試從而導(dǎo)致數(shù)據(jù)完全不可用。
過載保護(hù)設(shè)計(jì)(限流、熔斷、降級)
系統(tǒng)無法高可用的一個(gè)重要原因就在于,我們的系統(tǒng)經(jīng)常會(huì)有突發(fā)的流量過來,導(dǎo)致我們的服務(wù)超載運(yùn)行,這個(gè)時(shí)候,首先要做的當(dāng)然是快速擴(kuò)容,并且我們事先就要預(yù)留好一定的冗余。另外一個(gè)情況下,就算我們擴(kuò)容了,但是還是會(huì)超載,比如超過了下游依賴的存儲(chǔ)的最大容量、或者超過了下游依賴的三方服務(wù)的最大容量。那么這個(gè)時(shí)候,我們就需要執(zhí)行我們的過載保護(hù)策略了,主要包括限流、熔斷、降級,過載保護(hù)是為了保證服務(wù)部分可用從而不至于整個(gè)服務(wù)完全不可用。
- 限流。限流是指對進(jìn)入系統(tǒng)的請求進(jìn)行限流處理,如果請求量超過了我們系統(tǒng)最大處理能力或者超過了我們指定的處理能力,那么直接拒絕請求,通過這種丟棄部分請求的方式可以保證整個(gè)系統(tǒng)有一定的可用性,從而不至于讓整個(gè)系統(tǒng)完全不可用。怎么判別超過最大處理能力呢?一般就是針對 QPS 來判別,如果 QPS 超過閾值,那么就直接拒絕請求。
- 限流有很多細(xì)節(jié)的策略,比如針對接口限流、針對服務(wù)限流、針對用戶限流。
- 熔斷。熔斷,斷路(開路)的價(jià)值在于限制故障影響范圍。我們希望控制、減少或中斷和故障系統(tǒng)之間的通信,從而降低故障系統(tǒng)的負(fù)載,有利于系統(tǒng)的恢復(fù)。一般我們的服務(wù)都會(huì)有很多下游依賴,如果下游依賴的服務(wù)出現(xiàn)問題,比如開始超時(shí)甚至響應(yīng)非常慢的情況下,如果我們不做任何處理,那么會(huì)導(dǎo)致我們的整個(gè)請求都被卡住從而超時(shí),那么我們的業(yè)務(wù)服務(wù)對外就無法提供任何正常的功能了。為此,熔斷策略就可以解決這個(gè)問題,熔斷就是當(dāng)我們依賴的下游服務(wù)出現(xiàn)問題的時(shí)候,可以快速對其進(jìn)行熔斷(不發(fā)起請求),這樣我們的業(yè)務(wù)服務(wù)至少可以提供部分功能。熔斷的設(shè)計(jì)至少需要包括 熔斷請求判斷機(jī)制算法、熔斷恢復(fù)、熔斷告警 三部分
- 降級。降級是指我們劃分好系統(tǒng)的核心功能和非核心功能,然后當(dāng)我們的系統(tǒng)超過最大處理能力之后,直接關(guān)閉掉非核心的功能,從而保障核心功能的可用。關(guān)閉掉非核心的功能后可以使我們的系統(tǒng)釋放部分資源,從而可以有資源來處理核心功能。
- 熔斷和降級這兩個(gè)策略,看著比較像,字面的意思上來看都是要快速拒絕掉請求。但是他們是兩個(gè)維度的設(shè)計(jì),降級的目的是應(yīng)對系統(tǒng)自身的故障,而熔斷的目的是應(yīng)對我們系統(tǒng)依賴的外部服務(wù)故障的情況。
四、存儲(chǔ)層面
在當(dāng)前的互聯(lián)網(wǎng)時(shí)代,應(yīng)用服務(wù)基本都是無狀態(tài)的,因此應(yīng)用服務(wù)的高可用相對會(huì)比較簡單,但是對于數(shù)據(jù)存儲(chǔ)的高可用,相對來說,會(huì)復(fù)雜很多,因?yàn)閿?shù)據(jù)是有狀態(tài)的,那具體我們要怎么保障數(shù)據(jù)存儲(chǔ)的高可用,我們來分析下。
存儲(chǔ)層面的高可用方案的本質(zhì)都是通過通過數(shù)據(jù)冗余的方式來實(shí)現(xiàn)高可用,將數(shù)據(jù)復(fù)制到多個(gè)存儲(chǔ)介質(zhì)里面,可以有效的避免數(shù)據(jù)丟失,同時(shí)還可以提高并發(fā)能力,因?yàn)閿?shù)據(jù)是有狀態(tài)的,因此,這里會(huì)比服務(wù)的高可用要復(fù)雜很多,主要體現(xiàn)在如下幾個(gè)方面
- 數(shù)據(jù)如何復(fù)制?
- 各個(gè)節(jié)點(diǎn)的職責(zé)是什么?
- 如何應(yīng)對復(fù)制延遲?
- 如何應(yīng)對復(fù)制中斷?
常見的解決存儲(chǔ)高可用的方案有兩種:集群存儲(chǔ)和分布式存儲(chǔ)。業(yè)界大多是圍繞這些來構(gòu)建,或者是做相關(guān)衍生和擴(kuò)展。
集群存儲(chǔ)(集中式存儲(chǔ))
集群就是邏輯上處理同一任務(wù)的機(jī)器集合,可以屬于同一機(jī)房,也可分屬不同的機(jī)房。集群存儲(chǔ),就是把多臺(tái)機(jī)器上的存儲(chǔ)數(shù)據(jù)組合在一起對外形成一套統(tǒng)一的系統(tǒng)。集群存儲(chǔ)適合業(yè)務(wù)存儲(chǔ)量規(guī)模一般的場景,常規(guī)的業(yè)務(wù)數(shù)據(jù)存儲(chǔ)一般都是集群存儲(chǔ)方式就足夠了?,F(xiàn)在我們一般對于業(yè)務(wù)數(shù)據(jù)存儲(chǔ)的使用,默認(rèn)都是集群方式,比如 Redis、MySQL 等存儲(chǔ)類型,一般中大型互聯(lián)網(wǎng)公司,默認(rèn)肯定都是集群存儲(chǔ)的方式。
集群存儲(chǔ)就是我們常說的 1 主多備或者 1 主多從的架構(gòu),寫數(shù)據(jù)通過主機(jī),讀數(shù)據(jù)一般通過從機(jī)。集群存儲(chǔ)主要需要考慮如下幾個(gè)問題:
- 主機(jī)如何將數(shù)據(jù)復(fù)制給備機(jī)(從機(jī))
數(shù)據(jù)的寫入都是通過主機(jī),因此數(shù)據(jù)同步到備機(jī)(從機(jī)),就是要通過主機(jī)進(jìn)行數(shù)據(jù)復(fù)制到備機(jī)(從機(jī))。
還需要考慮主備同步的時(shí)間延遲問題。
- 備機(jī)(從機(jī))如何檢測主機(jī)狀態(tài)
- 主機(jī)故障后,備機(jī)(從機(jī))怎么切換為主機(jī)
主從架構(gòu)中,如果主機(jī)故障,可直接將備機(jī)(從機(jī))切換為主機(jī)
1.主備復(fù)制
主備復(fù)制是最常見也是最簡單的一種存儲(chǔ)高可用方案,幾乎所有的存儲(chǔ)系統(tǒng)都提供了主備復(fù)制的功能,例如 MySQL、Redis、MongoDB 等。
主備架構(gòu)中的“備機(jī)”主要還是起到一個(gè)備份作用,并不承擔(dān)實(shí)際的業(yè)務(wù)讀寫操作,如果要把備機(jī)改為主機(jī),需要人工操作。因此一般使用場景都是在一些內(nèi)部的后臺(tái)管理系統(tǒng)中使用。
2.主從復(fù)制
主從復(fù)制和主備復(fù)制雖然只有一字之差,但是兩者是不一樣的設(shè)計(jì)思路,“從”意思是“隨從、仆從”,“備”的意思是備份?!睆摹?的機(jī)制是要干活的,因此是承擔(dān)數(shù)據(jù)的“讀”操作的,一般就是主機(jī)負(fù)責(zé)讀寫操作,從機(jī)只負(fù)責(zé)讀操作,不負(fù)責(zé)寫操作。
3.主從切換
主備復(fù)制和主從復(fù)制方案存在兩個(gè)共性的問題:
- 主機(jī)故障后,無法進(jìn)行寫操作。
- 如果主機(jī)無法恢復(fù),需要人工指定新的主機(jī)角色。
主從切換(主備切換)就是為了解決這兩個(gè)問題而產(chǎn)生的,具體的設(shè)計(jì)就是在原有方案的基礎(chǔ)上增加“自動(dòng)切換”的能力,當(dāng)主機(jī)異常后,經(jīng)過系統(tǒng)檢測并且自動(dòng)將備機(jī)或者從機(jī)切換為主機(jī)。這個(gè)是實(shí)際應(yīng)用中比較多的一個(gè)方案之一,因?yàn)槲覀円欢軌蛴袡C(jī)制保證主機(jī)異常后從機(jī)能夠自動(dòng)切換為主機(jī)。
4.主主復(fù)制
主主復(fù)制指的是兩臺(tái)機(jī)器都是主機(jī),互相將數(shù)據(jù)復(fù)制給對方,客戶端可以任意挑選其中一臺(tái)機(jī)器進(jìn)行讀寫操作,如果采取主主復(fù)制架構(gòu),必須保證數(shù)據(jù)能夠雙向復(fù)制。這個(gè)相對來說,要求較高。
分布式存儲(chǔ)
集群指的是將幾臺(tái)服務(wù)器集中在一起,實(shí)現(xiàn)同一業(yè)務(wù)。而分布式是指將不同的業(yè)務(wù)分布在不同的地方,分布式中的每一個(gè)節(jié)點(diǎn),都可以做集群。
分布式存儲(chǔ)就是通過網(wǎng)絡(luò)使用企業(yè)中的每臺(tái)機(jī)器上的磁盤空間,并將這些分散的存儲(chǔ)資源構(gòu)成一個(gè)虛擬的存儲(chǔ)設(shè)備,數(shù)據(jù)分散的存儲(chǔ)在企業(yè)的各個(gè)角落。分布式存儲(chǔ)中的每臺(tái)服務(wù)器都可以處理讀寫請求,因此不存在集中式存儲(chǔ)中負(fù)責(zé)寫的主機(jī)那樣的角色。但在分布式存儲(chǔ)中,必須有一個(gè)角色來負(fù)責(zé)執(zhí)行數(shù)據(jù)分配算法,這個(gè)角色可以是獨(dú)立的一臺(tái)服務(wù)器,也可以是集群自己選舉出的一臺(tái)服務(wù)器。分布式存儲(chǔ)適合非常大規(guī)模的數(shù)據(jù)存儲(chǔ),業(yè)務(wù)數(shù)據(jù)量巨大的場景可以采用這種方式。常見的分布式存儲(chǔ)比如 Hadoop(HDFS)、HBase、Elasticsearch 等。
五、產(chǎn)品層面
產(chǎn)品層面的高可用架構(gòu)解決方案,基本上就是指我們的兜底產(chǎn)品策略。降級/限流的策略,更多的是從后端的業(yè)務(wù)服務(wù)和架構(gòu)上的設(shè)計(jì)來考慮相關(guān)解決方案。這里說的兜底策略,也可叫做柔性降級策略,更多則是通過產(chǎn)品層面上來考慮。
- 比如,當(dāng)我們的頁面獲取不到數(shù)據(jù)的時(shí)候,或者無法訪問的時(shí)候,要如何友好的告知用戶,比如【稍后重試】之類的。
- 比如 當(dāng)我們的真實(shí)的頁面無法訪問的時(shí)候,那么需要產(chǎn)品提供一個(gè)默認(rèn)頁面,如果后端無法獲取真實(shí)數(shù)據(jù),那么直接渲染默認(rèn)頁面。
- 比如服務(wù)器需要停機(jī)維護(hù),那么產(chǎn)品層面給一個(gè)停機(jī)頁面,所有用戶只會(huì)彈出這個(gè)停機(jī)頁面,不會(huì)請求后端服務(wù)
- 比如抽獎(jiǎng)商品給一個(gè)默認(rèn)兜底商品
- ...
六、運(yùn)維部署層面
開發(fā)階段-灰度發(fā)布、接口測試設(shè)計(jì)
灰度發(fā)布、接口測試、接口撥測系列設(shè)計(jì)包括但不限于:
- 灰度發(fā)布,我們服務(wù)發(fā)布上線的時(shí)候,要有一個(gè)灰度的過程,先灰度 1-2 個(gè)服務(wù)實(shí)例,然后逐步放量觀察,如果一切 ok,再逐步灰度,直到所有實(shí)例發(fā)布完畢
- 接口測試,每次服務(wù)發(fā)布上線的時(shí)候,服務(wù)提供的各種接口,都要有接口測試用例,接口測試用例跑過之后,服務(wù)才能發(fā)布上線,目的是為了查看我們對外提供的接口是否能夠正常,避免服務(wù)發(fā)布上線后才發(fā)現(xiàn)有問題
灰度發(fā)布和接口測試,一般在大公司里面會(huì)有相關(guān)的 DevOps 流程來保證。
開發(fā)階段-監(jiān)控告警設(shè)計(jì)
監(jiān)控告警的設(shè)計(jì),在大公司來說,根本不是問題,因?yàn)橐欢〞?huì)有比較專門一撥人去做這種基礎(chǔ)能力的建設(shè),會(huì)有對應(yīng)的配套系統(tǒng),業(yè)務(wù)開發(fā)的同學(xué)只需要配置或使用即可。那如果說公司內(nèi)部沒有相關(guān)基礎(chǔ)建設(shè),那么就需要自己分別來接入對應(yīng)的系統(tǒng)了。
監(jiān)控系統(tǒng)
一般在監(jiān)控系統(tǒng)這方面的開源解決方案包括但不限于這些:
- ELK (Elasticsearch、Logstash、Kibana) 日志收集和分析
我們的日志記錄不能都本地存儲(chǔ),因?yàn)槲⒎?wù)化后,日志散落在很多機(jī)器上,因此必須要有一個(gè)遠(yuǎn)程日志記錄的系統(tǒng),ELK 是不二人選
- Prometheus 監(jiān)控收集
可以監(jiān)控各種系統(tǒng)層面的指標(biāo),包括自定義的一些業(yè)務(wù)指標(biāo)
- OpenTracing 分布式全鏈路追蹤
一個(gè)請求的上下游這么多服務(wù),怎么能夠把一個(gè)請求的上下游全部串起來,那么就要依靠 OpenTracing,可以把一個(gè)請求下的所有鏈路都串起來并且有詳細(xì)的記錄
- OpenTelemetry 可觀測系統(tǒng)標(biāo)準(zhǔn)
最新的標(biāo)準(zhǔn),大一統(tǒng),集合了跟蹤數(shù)據(jù)(Traces),指標(biāo)數(shù)據(jù)(Metrics),日志數(shù)據(jù)(Logs)來觀測分布式系統(tǒng)狀態(tài)的能力
我們會(huì)依托開源系統(tǒng)進(jìn)行自建或者擴(kuò)展,甚至直接使用都行,然后我們的監(jiān)控的指標(biāo)一般會(huì)包括:
- 基礎(chǔ)設(shè)施層的監(jiān)控:主要是針對網(wǎng)絡(luò)、交換機(jī)、路由器等低層基礎(chǔ)設(shè)備,這些設(shè)備如果出現(xiàn)問題,那么依托其運(yùn)行的業(yè)務(wù)服務(wù)肯定就無法穩(wěn)定的提供服務(wù),我們常見的核心監(jiān)控指標(biāo)包括網(wǎng)絡(luò)流量(入和出)、網(wǎng)絡(luò)丟包情況、網(wǎng)絡(luò)連接數(shù)等。
- 操作系統(tǒng)層的監(jiān)控:這里需要包含物理機(jī)和容器。常見的核心指標(biāo)監(jiān)控包括 CPU 使用率、內(nèi)存占用率、磁盤 IO 和網(wǎng)絡(luò)帶寬等。
- 應(yīng)用服務(wù)層的監(jiān)控:這里的指標(biāo)會(huì)比較多,核心的比如主調(diào)請求量、被調(diào)請求量、接口成功率、接口失敗率、響應(yīng)時(shí)間(平均值、P99、P95 等)等。
- 業(yè)務(wù)內(nèi)部的自定義監(jiān)控:每個(gè)業(yè)務(wù)服務(wù)自己的一些自定義的監(jiān)控指標(biāo)。比如電商系統(tǒng)這里的:瀏覽、支付、發(fā)貨等各種情況的業(yè)務(wù)指標(biāo)
- 端用戶層的監(jiān)控:前面的監(jiān)控更多的都是內(nèi)部系統(tǒng)層面的,但是用戶真正訪問到頁面,中間還有外網(wǎng)的情況,用戶真正獲取到數(shù)據(jù)的耗時(shí)、打開頁面的耗時(shí)等這些信息也是非常重要的,但是這個(gè)一般就是需要客戶端或者前端去進(jìn)行統(tǒng)計(jì)了。
告警系統(tǒng)
這些系統(tǒng)接入完了之后,還只是做到監(jiān)控和統(tǒng)計(jì),當(dāng)出現(xiàn)問題的時(shí)候,還需要進(jìn)行實(shí)時(shí)告警,因此還要有一個(gè)實(shí)時(shí)告警系統(tǒng),如果沒有實(shí)時(shí)報(bào)警,系統(tǒng)運(yùn)行異常后我們就無法快速感知,這樣就無法快速處理,就會(huì)給我們的業(yè)務(wù)帶來重大故障和災(zāi)難。告警設(shè)計(jì)需要包括:
- 實(shí)時(shí)性:實(shí)現(xiàn)秒級監(jiān)控;
- 全面性:覆蓋所有系統(tǒng)業(yè)務(wù);
- 實(shí)用性:預(yù)警分為多個(gè)級別,監(jiān)控人員可以方便實(shí)用地根據(jù)預(yù)警嚴(yán)重程度做出精確的決策;
- 多樣性:預(yù)警方式提供推拉模式,包括短信,郵件,可視化界面,方便監(jiān)控人員及時(shí)發(fā)現(xiàn)問題
開發(fā)階段-安全性、防攻擊設(shè)計(jì)
安全性、防攻擊設(shè)計(jì)的目的是為了防刷、防黑產(chǎn)、防黑客,避免被外部惡意攻擊,這個(gè)一般有幾個(gè)策略:
- 在公司級別的流量入口做好統(tǒng)一的防刷和鑒權(quán)的能力,比如再統(tǒng)一接入層做好封裝
- 在業(yè)務(wù)服務(wù)內(nèi)部,做好相關(guān)的業(yè)務(wù)鑒權(quán),比如登錄態(tài)信息、比如增加業(yè)務(wù)鑒權(quán)的邏輯
部署階段-多機(jī)房部署(容災(zāi)設(shè)計(jì))
一般的高可用策略,都是針對一個(gè)機(jī)房內(nèi)來服務(wù)層面來設(shè)計(jì)的,但是如果整個(gè)機(jī)房都不可用了,比如地震、火災(zāi)、光纖挖斷等。。。。那么這個(gè)情況怎么辦?這就需要我們的服務(wù)和存儲(chǔ)都能夠進(jìn)行容災(zāi)了,容災(zāi)的一個(gè)常見方案就是多機(jī)房部署了。
- 服務(wù)的多機(jī)房部署,這個(gè)比較容易,因?yàn)槲覀兊姆?wù)都是無狀態(tài)的,因此只要名字服務(wù)能夠發(fā)現(xiàn)不同機(jī)房的服務(wù),就可以實(shí)現(xiàn)調(diào)用,這里需要注意的是名字服務(wù)(或者說負(fù)載均衡服務(wù))要能夠有就近訪問的能力。
- 存儲(chǔ)的多機(jī)房部署,這個(gè)會(huì)比較難搞一點(diǎn),因?yàn)榇鎯?chǔ)是有狀態(tài)的,部署在不同的機(jī)房就涉及到存儲(chǔ)的同步和復(fù)制問題。
條件不允許的情況下,我們保證多機(jī)房部署業(yè)務(wù)服務(wù)就可以了。
線上運(yùn)行階段-故障演練(混沌實(shí)驗(yàn))
故障演練在大公司是一個(gè)常見的手段;在業(yè)界,Netflix 早在 2010 年就構(gòu)建了混沌實(shí)驗(yàn)工具 Chaos Monkey,混沌實(shí)驗(yàn)工程對于提升復(fù)雜分布式系統(tǒng)的健壯性和可靠性發(fā)揮了重要作用。
簡單的故障演練就是模擬機(jī)房斷電、斷網(wǎng)、服務(wù)掛掉等場景,然后看我們的整個(gè)系統(tǒng)運(yùn)行是否正常。系統(tǒng)的就要參考混沌實(shí)驗(yàn)工程來進(jìn)行詳細(xì)的規(guī)劃和設(shè)計(jì),這個(gè)是一個(gè)相對比較大的工程,效果挺好,但是需要有大量人力去開發(fā)這種基礎(chǔ)建設(shè)。
線上運(yùn)行階段-接口撥測系列設(shè)計(jì)
接口撥測,和巡檢類似,就是服務(wù)上線后,每隔一個(gè)固定時(shí)間(比如 5s)調(diào)用后端的各種接口,如果接口異常則進(jìn)行告警
針對接口撥測,一般也會(huì)有相關(guān)配套設(shè)施來提供相關(guān)的能力去實(shí)現(xiàn),如果沒有提供,那么我們可以自己寫一個(gè)接口撥測(巡檢)的服務(wù),定期去調(diào)用重要的接口。
七、異常應(yīng)急層面
前面做了這么多保障,但是終究架不住線上的各種異常情況,如果真出問題了,讓我們的服務(wù)異常,無法提供服務(wù)后,我們還需要最后一根救命稻草,那就是應(yīng)急預(yù)案,將服務(wù)異常的損失降低到最小。
應(yīng)急預(yù)案就是我們需要事先規(guī)劃好,我們業(yè)務(wù)系統(tǒng)在各個(gè)層級出現(xiàn)問題后,我們需要第一時(shí)間怎么恢復(fù),制定好相關(guān)規(guī)則和流程,當(dāng)出現(xiàn)異常狀況后可以按照既有的流程去執(zhí)行,這樣避免出現(xiàn)問題后手忙腳亂導(dǎo)致事態(tài)擴(kuò)大。