Design for failure常見的12種設(shè)計(jì)思想
本文轉(zhuǎn)載自微信公眾號「架構(gòu)精進(jìn)之路」,作者張張 。轉(zhuǎn)載本文請聯(lián)系架構(gòu)精進(jìn)之路公眾號。
hello,大家好,我是張張,「架構(gòu)精進(jìn)之路」公號作者。
通常情況下,我們的一個(gè)請求會經(jīng)過三個(gè)服務(wù)來處理。
請求從客戶端發(fā)出,到達(dá)Proxy Layer(執(zhí)行一些公共的邏輯,如邏輯、流控、審計(jì)等),完成后,發(fā)往App Layer(執(zhí)行具體業(yè)務(wù)邏輯),執(zhí)行完畢后,發(fā)向Data Laye(進(jìn)行數(shù)據(jù)持久化)。
事情看起來很簡單,然而,在一個(gè)分布式系統(tǒng)中:出錯(cuò)是常態(tài)。
因此,我們需要:Design For Failure。即當(dāng)你的系統(tǒng)將錯(cuò)誤當(dāng)作正常流時(shí),系統(tǒng)便已經(jīng)對錯(cuò)誤免疫了。
在此,跟大家介紹常見的12種設(shè)計(jì)思想。
1、防御性設(shè)計(jì)(Defensive Design)
所謂的防御性設(shè)計(jì)實(shí)際上就是“防呆”,英文叫Idiot Proofing。說白了就是用戶有時(shí)候會不自覺的做一些蠢事,我們在設(shè)計(jì)的時(shí)候要盡量考慮到一些不規(guī)范的交互行為,如果你的用戶是一只猴子,你要寫包單保證系統(tǒng)不被玩壞。
例如,在Android開發(fā)中使用到的Monkey Test就是用于這樣的目的。
2、邊界情況(Edge Case)
這個(gè)設(shè)計(jì)思想在測試領(lǐng)域比較常見,就是我們在設(shè)計(jì)我們的設(shè)計(jì)案例的時(shí)候有沒有充分考慮在邊界情況下的系統(tǒng)行為。
比較常見的例如,閏年情況、跨日情況等邊界。
3、防誤措施(Mistake Proofing)
怎么保證不會發(fā)生錯(cuò)誤。例如在人機(jī)交互環(huán)節(jié),能不能進(jìn)行輸入校驗(yàn)?
4、解耦(Decoupling)
設(shè)計(jì)的時(shí)候,哪怕是最基礎(chǔ)的代碼也應(yīng)該符合開閉原則。
Spring的IOC就是為了把對象創(chuàng)建及維護(hù)從原來的由引用類負(fù)責(zé)這種強(qiáng)耦合模式轉(zhuǎn)成通過spring容器負(fù)責(zé)。且解耦一般的做法是通過把內(nèi)部邏輯封裝起來,暴露對外統(tǒng)一API接口,調(diào)用方不需要了解被調(diào)用方的內(nèi)部邏輯實(shí)現(xiàn),只需要知道提供什么功能即可。
再引申一下,解耦的作用就在于復(fù)用,把所有的高內(nèi)聚功能獨(dú)立成一個(gè)個(gè)模塊,然后就可以像樂高積木一樣根據(jù)調(diào)用方的實(shí)際需求進(jìn)行組裝。
5、冗余(Redundancy)
所謂的冗余指的通過重復(fù)配置關(guān)鍵組件或部件,保證在關(guān)鍵組件失效的情況下還有備份組件運(yùn)作以便保證系統(tǒng)可以繼續(xù)提供服務(wù)。生活中的例子請參與飛機(jī)的雙引擎設(shè)計(jì)。
主從模式就是冗余的體現(xiàn)。在正常情況下,主實(shí)例負(fù)責(zé)提供全部的服務(wù),從實(shí)例在主實(shí)例整體或部分不可用的情況下,完全替代主實(shí)例整體或局部而對外提供服務(wù)。
6、重試(Retry)
重試是在分布式系統(tǒng)下處理瞬態(tài)故障的一個(gè)基本手段,簡單有效(當(dāng)然重試的前提是要求冪等)。但是重試也是可以很危險(xiǎn)的,它能夠引起把一個(gè)局部小時(shí)間迅速升級為一個(gè)系統(tǒng)重大故障,嚴(yán)重者導(dǎo)致系統(tǒng)假死。
舉個(gè)簡單例子:如果我們的鏈路類似上圖,這里會發(fā)生什么問題?
在極端情況下,重試次數(shù)達(dá)到5*5*5*5=625次。
當(dāng)鏈路中的其中一個(gè)服務(wù)故障率異常的時(shí)候,那重試風(fēng)暴便開啟了,因?yàn)橹卦嚍榉?wù)器帶來額外的開銷和線程的占用,然后其他新來的請求又形成排隊(duì),這樣的話就形成了類似的DDos惡性事件。
7、冷備(Cold Standby)
冷備實(shí)際上也是冗余設(shè)計(jì)的其中一種體現(xiàn),只是它會更側(cè)重于“冷”,意思是當(dāng)系統(tǒng)發(fā)生宕機(jī)時(shí),這個(gè)系統(tǒng)是需要手動(dòng)啟動(dòng)用于替換下線的主實(shí)例,它是跟熱備是不一樣,熱備更多體現(xiàn)在自動(dòng)切換。
8、熔斷(Derating)
熔斷本質(zhì)上就是一種防御性設(shè)計(jì)或者策略。假設(shè)一個(gè)微服務(wù)體系下的系統(tǒng),其中A服務(wù)調(diào)用B服務(wù)。系統(tǒng)的QPS是千級別,當(dāng)時(shí)如果B服務(wù)掛掉的話A的線程絕對在短時(shí)間內(nèi)占滿耗盡而導(dǎo)致假死,從而形成大量A請求積壓而導(dǎo)致情況惡化,最終形成雪崩。
9、容錯(cuò)(Error Tolerance)
狹義的容錯(cuò)泛指人機(jī)交互界面的時(shí)候需要對用戶輸入進(jìn)行輸入校驗(yàn),保證數(shù)據(jù)準(zhǔn)確性。
廣義的容錯(cuò)應(yīng)該是兩個(gè)具有明確邊界的事物(如服務(wù)間,系統(tǒng)間)交互時(shí)候針對可能發(fā)生的一切主客觀異常情況的防御性手段。常見的容錯(cuò)機(jī)制有failsafe、failback、failover、failfast。
- failfast 更多指的是快速失敗,避免線程積壓導(dǎo)致系統(tǒng)滾雪球式崩潰。
- failover 指的是失效轉(zhuǎn)移。
- failsafe 指的是失效安全。
- failback 指的是失效自動(dòng)恢復(fù),將故障實(shí)例切換到備實(shí)例。
10、失效安全(Fail safe)
所謂的失效安全,就是指在特定失效的情況下,一個(gè)系統(tǒng)或者服務(wù)也不會對業(yè)務(wù)造成損害。
例如:我們使用token進(jìn)行安全登錄也是一種失效安全的體現(xiàn),如果token失效了(如時(shí)間過期),用戶是無法登錄的,因?yàn)檎5卿浶枰猼oken有一種約束因素,這種因素就是時(shí)間。如果時(shí)間過了,代表這種約束因素不存在或者不再有效了,登錄功能就不能正常工作了。
11、優(yōu)雅降級(Graceful Degradation)
服務(wù)降級跟熔斷還是挺像的,只是降級來得更加溫和和優(yōu)雅一點(diǎn)。熔斷是直接斷掉防止異常進(jìn)一步擴(kuò)大而導(dǎo)致雪崩,但是我們的終極目標(biāo)是提供盡可能多的服務(wù),這個(gè)就是優(yōu)雅降級的理念。在一些異常情況或者秒殺場景下,為了保證核心服務(wù)(如商品下單、支付)的正??捎?,會放棄掉一些非核心服務(wù)(如歷史賬單查詢),這就是所謂的服務(wù)降級。
在微服務(wù)框架中,一般會使用Hystrix的@HystrixCommand或Feign的@FeignClient對服務(wù)進(jìn)行聲明,然后為每個(gè)服務(wù)配置相應(yīng)的fallback類,最終結(jié)合起來進(jìn)行服務(wù)降級。
12、耐用性(Durability)
這里我理解的是系統(tǒng)或數(shù)據(jù)的耐受性。
例如數(shù)據(jù),為什么我們一定要持久化到數(shù)據(jù)庫,因?yàn)榫褪且柚鷶?shù)據(jù)庫硬件各種維度的耐受性。
補(bǔ)充
作為一名designer或者developer,應(yīng)該要對墨菲定律心存敬畏。
另外,需要額外補(bǔ)充一點(diǎn)的就是:監(jiān)控(Monitoring)。
我們的系統(tǒng)有哪幾個(gè)緯度的監(jiān)控,估計(jì)最多就是常規(guī)的硬件狀態(tài)監(jiān)控。當(dāng)然這里的監(jiān)控我理解除了技術(shù)指標(biāo)監(jiān)控,還更應(yīng)該有業(yè)務(wù)指標(biāo)監(jiān)控,否則我們都在裸泳,等海水退下去后就一覽無遺。
監(jiān)控實(shí)際上是為了更好的主動(dòng)防御,一套完善的告警監(jiān)控系統(tǒng),能夠快速通知開發(fā)與運(yùn)維,開發(fā)側(cè)能夠完成緊急修復(fù)并能夠協(xié)同運(yùn)維進(jìn)行快速部署。