淺談微服務(wù)雪崩及應(yīng)對措施
作者:NoTryNoSuccess
來源:SegmentFault思否社區(qū)
微服務(wù)的高內(nèi)聚和低耦合
首先提一下微服務(wù)的一個(gè)劃分原則:高內(nèi)聚和低耦合。
高內(nèi)聚:相關(guān)度比較高的部分盡可能的集中,不要分散。
低耦合:兩個(gè)相關(guān)的模塊盡可能降低依賴。
應(yīng)該說,模塊化、低耦合一直以來都是軟件設(shè)計(jì)追求的目標(biāo),獨(dú)立部署的微服務(wù)使模塊之間的依賴關(guān)系更加清晰,也更加隔離,使系統(tǒng)易于開發(fā)、維護(hù),代表了正確的技術(shù)方向。但需要注意的是低耦合的兩個(gè)微服務(wù)之間還是存在著一定的請求調(diào)用關(guān)系,否則就是零耦合了。既然請求調(diào)用關(guān)系很多時(shí)候避免不了,那么當(dāng)一個(gè)復(fù)雜系統(tǒng)劃分了很多微服務(wù)后,服務(wù)間請求調(diào)用鏈在一定程度上將增加系統(tǒng)維護(hù)、問題排查的復(fù)雜性。
服務(wù)調(diào)用鏈

如上圖,服務(wù)間存在多個(gè)內(nèi)部調(diào)用,這里形成兩條請求鏈路,一是服務(wù) A => 服務(wù) B => 服務(wù) C => 服務(wù) D,二是服務(wù) F => 服務(wù) E => 服務(wù) B。可以看到服務(wù) B 是兩條調(diào)用鏈間的交界點(diǎn),同時(shí)也依賴了其他多個(gè)服務(wù)。

級聯(lián)故障和雪崩
服務(wù) B 作為服務(wù) A 和服務(wù) F 的兩個(gè)流量入口必經(jīng)之處,可能是一個(gè)公共服務(wù),但它也依賴了其他多個(gè)服務(wù)。假設(shè)服務(wù) C 或服務(wù) D 其中一個(gè)有問題,在沒有熔斷措施的情況下,就會(huì)出現(xiàn)級聯(lián)故障,系統(tǒng)逐漸崩盤,最終導(dǎo)致雪崩。
一種常見的情況是服務(wù) D 所依賴的外部接口出現(xiàn)了故障但自己沒有做任何控制或防護(hù)措施,因此擴(kuò)散到了所有調(diào)用它的服務(wù),自然也就包含服務(wù) B。以這個(gè)例子來講,其實(shí)外部接口出現(xiàn)故障并不是最大的問題,最大的問題很可能是服務(wù) D 沒有針對外部接口調(diào)用設(shè)置 timeout 超時(shí),從而造成上層大量請求等待堆積,請求數(shù)得不到釋放,系統(tǒng)負(fù)載過高最終導(dǎo)致服務(wù)崩潰。不過光有 timeout 超時(shí)機(jī)制是不夠的,下文會(huì)提到更進(jìn)一步的措施。
應(yīng)對措施
在我看來,分布式微服務(wù)系統(tǒng)要避免雪崩,由單個(gè)微服務(wù)來分析可以從兩個(gè)方面考慮:一是微服務(wù)自身的高可用及抗壓能力,要能抗住流量洪峰;二是當(dāng)該微服務(wù)所直接依賴的其他微服務(wù)出現(xiàn)故障時(shí),自己能否優(yōu)雅地處理。有點(diǎn)類似于人人做好自己(其實(shí)第二個(gè)方面的要求已經(jīng)不光是做好自己了,還要求當(dāng)與自己有密切關(guān)系的其他人瘋了后不要讓自己也跟著瘋了,盡量降低對自己的影響),社會(huì)自然就穩(wěn)定的道理。
服務(wù)限流
從提升微服務(wù)自身素質(zhì)這一點(diǎn)來講,每個(gè)微服務(wù)首先應(yīng)盡量保證自身健壯性、穩(wěn)定性。如果你的微服務(wù)是要應(yīng)對高并發(fā)的,有三把利器用來保護(hù)系統(tǒng):緩存、降級和限流。緩存的目的是提升系統(tǒng)訪問速度及吞吐量,可謂是抗高并發(fā)流量的銀彈;而降級是當(dāng)服務(wù)出問題或者影響到核心流程的性能時(shí)需要暫時(shí)屏蔽掉,待高峰或者問題解決后再打開;而有些場景并不能用緩存和降級來解決,比如稀缺資源(秒殺、搶購)、寫服務(wù)(如評論、下單)、頻繁的復(fù)雜查詢(評論的最后幾頁)等,因此需有一種手段來限制這些場景的并發(fā)/請求量,即限流。
限流的目的是通過對并發(fā)訪問/請求進(jìn)行限速或者一個(gè)時(shí)間窗口內(nèi)的請求進(jìn)行限速來保護(hù)系統(tǒng),一旦達(dá)到限制速率則可以拒絕服務(wù)(定向到錯(cuò)誤頁或告知資源沒有了)、排隊(duì)或等待(比如秒殺、評論、下單)、降級(返回兜底數(shù)據(jù)或默認(rèn)數(shù)據(jù),如商品詳情頁庫存默認(rèn)有貨)。
一般開發(fā)高并發(fā)系統(tǒng)常見的限流有:限制總并發(fā)數(shù)(比如數(shù)據(jù)庫連接池、線程池)、限制瞬時(shí)并發(fā)數(shù)(如 Nginx 的 limit_conn 模塊,用來限制瞬時(shí)并發(fā)連接數(shù))、限制時(shí)間窗口內(nèi)的平均速率(如 Nginx 的 limit_req 模塊,限制每秒的平均速率)。其他還有如限制遠(yuǎn)程接口調(diào)用速率、限制消息隊(duì)列的消費(fèi)速率,另外還可以根據(jù)網(wǎng)絡(luò)連接數(shù)、網(wǎng)絡(luò)流量、CPU 或內(nèi)存負(fù)載等來限流。
服務(wù)熔斷
從另一個(gè)方面來講,微服務(wù)自身是完善穩(wěn)定了,但如果其依賴的其他微服務(wù)出現(xiàn)不可控的故障時(shí),就可以順其自然,放任不管了嗎?
這就涉及到熔斷機(jī)制了,熔斷這一概念來源于電子工程中的斷路器(Circuit Breaker)。熔斷機(jī)制是應(yīng)對雪崩效應(yīng)的一種微服務(wù)鏈路保護(hù)機(jī)制。我們在各種場景下都會(huì)接觸到熔斷這兩個(gè)字。高壓電路中,如果某個(gè)地方的電壓過高,熔斷器就會(huì)熔斷,對電路進(jìn)行保護(hù)。股票交易中,如果股票指數(shù)過高,也會(huì)采用熔斷機(jī)制,暫停股票的交易。
同樣,在微服務(wù)架構(gòu)中,熔斷機(jī)制也是起著類似的作用。在互聯(lián)網(wǎng)系統(tǒng)中,當(dāng)下游服務(wù)因訪問壓力過大而響應(yīng)變慢或失敗,上游服務(wù)為了保護(hù)系統(tǒng)整體的可用性,可以暫時(shí)切斷對下游服務(wù)的調(diào)用。這種犧牲局部,保全整體的措施就叫做熔斷。當(dāng)請求調(diào)用鏈中的某個(gè)微服務(wù)不可用或者響應(yīng)時(shí)間太長時(shí),會(huì)進(jìn)行服務(wù)的降級,開啟熔斷狀態(tài),熔斷該節(jié)點(diǎn)微服務(wù)的調(diào)用,快速返回錯(cuò)誤的響應(yīng)信息。當(dāng)檢測到該節(jié)點(diǎn)微服務(wù)調(diào)用響應(yīng)正常后,關(guān)閉熔斷狀態(tài),恢復(fù)正常的請求調(diào)用。
服務(wù)降級
服務(wù)降級其實(shí)可以分為兩種性質(zhì)的降級:
從自身流量保護(hù)角度。比如當(dāng)整個(gè)微服務(wù)架構(gòu)整體的負(fù)載超出了預(yù)設(shè)的上限閾值或即將到來的流量預(yù)計(jì)將會(huì)超過預(yù)設(shè)的閾值時(shí),為了保證重要或基本的服務(wù)能正常高效運(yùn)行,我們可以將一些不重要或不緊急的服務(wù)或任務(wù)進(jìn)行服務(wù)的延遲使用或暫停使用,亦或是對服務(wù)中的某個(gè)模塊進(jìn)行閹割或者替換為簡單實(shí)現(xiàn)版本。 從優(yōu)雅應(yīng)對所依賴的服務(wù)故障角度。與上面服務(wù)熔斷相對應(yīng),當(dāng)服務(wù)所依賴的其他服務(wù)發(fā)生故障時(shí),會(huì)開啟熔斷狀態(tài),比如后續(xù)對該服務(wù)接口的調(diào)用不再經(jīng)過網(wǎng)絡(luò),執(zhí)行本地的默認(rèn)方法或者對上層返回友好的錯(cuò)誤提示等,達(dá)到服務(wù)降級的效果。

