架構(gòu)設(shè)計(jì)容錯(cuò)篇之重試

avatar

概述

在微服務(wù)架構(gòu)中,服務(wù)之間的調(diào)用并不像方法之間的調(diào)用那么穩(wěn)定,它可能會(huì)因網(wǎng)絡(luò)、磁盤、內(nèi)存、CPU等硬件原因?qū)е抡{(diào)用失敗,也有可能是服務(wù)自身問(wèn)題諸如調(diào)用超時(shí)、服務(wù)繁忙、服務(wù)不可用導(dǎo)致調(diào)用失敗。
調(diào)用失敗的原因各種各樣,有些已經(jīng)超出了我們的控制范圍如網(wǎng)絡(luò)抖動(dòng),丟包,也有些是我們提高程序處理能力可以有效減少的如超時(shí),還有一些是短暫的、臨時(shí)的失敗不久之后可能會(huì)馬上恢復(fù)諸如:網(wǎng)絡(luò)抖動(dòng)、服務(wù)繁忙、CPU高負(fù)載等等。
盡管有些失敗不可控,但為了提高系統(tǒng)的穩(wěn)定性,有些短暫的失敗還是可以通過(guò)重試使其恢復(fù)正常的。

問(wèn)題

在有些重要的服務(wù)調(diào)用中,如:支付服務(wù)調(diào)用訂單服務(wù)通知其訂單支付成功,如果服務(wù)調(diào)用失敗且沒(méi)有任何的補(bǔ)救措施,直接造成的影響就是用戶付款了但收不到商品。
而失敗的原因很可能是由于訂單服務(wù)正在進(jìn)行GC垃圾回收導(dǎo)致的請(qǐng)求超時(shí),亦或者是訂單服務(wù)負(fù)載過(guò)高本請(qǐng)求被拒絕處理。
不管怎么說(shuō),訂單服務(wù)很可能在幾秒之內(nèi)恢復(fù)到正常狀態(tài),能夠繼續(xù)處理外部服務(wù)的請(qǐng)求。
所以,當(dāng)失敗是暫時(shí)的不久之后可恢復(fù)的,那么我們?cè)谑≈筮M(jìn)行重試,顯然是一個(gè)可行的方案。

方案

針對(duì)可重試的失敗,通常我們有下面幾種常用的重試策略。

立即重試

如果失敗的原因是瞬時(shí)的、偶發(fā)的,那么我們可以在調(diào)用失敗后立即重試進(jìn)行重試,以最快的速度自動(dòng)恢復(fù),從而降低系統(tǒng)的錯(cuò)誤率。
比如,因網(wǎng)絡(luò)瞬時(shí)抖動(dòng)、數(shù)據(jù)庫(kù)更新被鎖定產(chǎn)生的調(diào)用失敗,只要我們立即重試大概率就會(huì)成功。
但是,重試的次數(shù)不宜過(guò)多,因?yàn)榱⒓粗卦嚂?huì)占用當(dāng)前線程,線程得不到即時(shí)釋放會(huì)增加服務(wù)自身的負(fù)載。

延遲重試

相反,如果原因不是瞬時(shí)而需要一小段時(shí)間系統(tǒng)才能恢復(fù)正常,那么我們可以在調(diào)用失敗后每間隔一段時(shí)間重試一次,從而提高重試的成功率。
比如,服務(wù)提供方繁忙請(qǐng)求被拒、服務(wù)發(fā)版導(dǎo)致的短暫不可用,這些基本上可以通過(guò)延遲重試來(lái)使調(diào)用恢復(fù)正常。
既然,無(wú)法確定錯(cuò)誤恢復(fù)的時(shí)長(zhǎng),那應(yīng)該如何設(shè)置重試間隔時(shí)間?這就涉及到退避策略的選擇了,常見(jiàn)的退避策略有下面這幾種:

線性退避:調(diào)用失敗后等待固定的時(shí)間后進(jìn)行重試,直到重試次數(shù)耗盡;

隨機(jī)退避:重試間隔時(shí)間不是固定的,每次間隔可能都不一樣;

指數(shù)退避:調(diào)用失敗后等待 2^x * n(n為重試間隔,x為重試次數(shù))后,進(jìn)行重試操作,這種退避策略可以有效的減少無(wú)效的重試。

但是,延遲重試如果延遲總時(shí)長(zhǎng)過(guò)長(zhǎng),一般都需要進(jìn)行異步處理,這種策略比較適合不依賴返回值的情況,如上面的訂單通知。

異常分類重試

這是一種組合策略。為了更恰當(dāng)?shù)奶幚硎〉恼{(diào)用,我們可以對(duì)引發(fā)失敗的原因即程序異常進(jìn)行分類,明確那些異常是可以重試的,那些是沒(méi)有必要重試的,以及那些是可以立即重試的,那些是需要延遲重試的。
區(qū)分好后,就可以根據(jù)異常的重試特征選擇要不要重試,如果是可重試的那么再進(jìn)一步根據(jù)是否能立即重試從上面兩個(gè)策略中選擇一個(gè)合適的策略。

影響

雖然重試可以減少服務(wù)的錯(cuò)誤率,但如果使用不當(dāng)也會(huì)產(chǎn)生負(fù)面的影響,下面是在微服務(wù)架構(gòu)中,使用重試常見(jiàn)的問(wèn)題。

嵌套的重試

假設(shè)我們有三個(gè)服務(wù)分別是A、B、C,它們之間的依賴關(guān)系是A依賴B,B依賴C,A,B服務(wù)在調(diào)用下游服務(wù)的時(shí)候都使用了重試機(jī)制,并且重試次數(shù)都設(shè)置了3次。
如果很不幸C處于繁忙狀態(tài),導(dǎo)致所有的重試最終都以失敗告終,即A第一次重試時(shí),B重試三次都是失?。籄第二次重試時(shí),B重試三次也失敗;A最后一次重試時(shí),B重試三次還是失敗。
那么這樣的重試的次數(shù)便是指數(shù)級(jí)別的,位于最后一層的服務(wù)C被調(diào)用的次數(shù)便是其它服務(wù)重試次數(shù)的乘積,即9=3x3。
上面調(diào)用鏈上的重試機(jī)制是嵌套的,這種嵌套重試在最后一層服務(wù)不可用或繁忙時(shí),其影響是災(zāi)難性的,會(huì)瞬間引發(fā)雪崩效應(yīng)。
我們可以假設(shè)每秒的訪問(wèn)量是y,鏈路層級(jí)是l,每個(gè)服務(wù)的重試次數(shù)是n,那么重試的次數(shù)就可以通過(guò)該公式算出:y*n^(l-1)。比如,某個(gè)系統(tǒng)的每秒訪問(wèn)量是100,服務(wù)之間的鏈路層級(jí)是5層,每個(gè)服務(wù)的重試次數(shù)是3次,那么最后一層不可用的服務(wù)每秒的訪問(wèn)量便是8100次。

大多數(shù)公司都會(huì)使用第三方的微服務(wù)框架,那么可以通過(guò)框架的協(xié)議自下而上的透徹一個(gè)字段如retry-not-allowed,來(lái)標(biāo)示上層服務(wù)不能進(jìn)行重試。
也就是說(shuō),下層服務(wù)重試失敗后,通過(guò)協(xié)議將該字段傳遞給調(diào)用它的服務(wù)消費(fèi)者,服務(wù)消費(fèi)者收到該字段就取消自己的重試機(jī)制,如此這般直至最上層的服務(wù)。
這樣就可以避免嵌套重試的潛在風(fēng)險(xiǎn),但是還有一個(gè)問(wèn)題要注意,那就是超時(shí)的問(wèn)題。
如果上層服務(wù)的調(diào)用的超時(shí)時(shí)間低于下層服務(wù)的調(diào)用超時(shí)時(shí)間,那么會(huì)造成下層服務(wù)的重試還在執(zhí)行中,而上層服務(wù)就因?yàn)檎{(diào)用超時(shí)開(kāi)始了下一次的重試。
這個(gè)問(wèn)題同樣也可以通過(guò)協(xié)議來(lái)解決,最上層的服務(wù)將自己的超時(shí)時(shí)間傳遞給下層服務(wù),下層服務(wù)的超時(shí)時(shí)間只能在上層服務(wù)的超時(shí)范圍之內(nèi),當(dāng)然要減掉一些消耗時(shí)間如網(wǎng)絡(luò)傳輸時(shí)間。

但是,如果下游服務(wù)長(zhǎng)時(shí)間不可用,這樣不停的重試也是會(huì)增加系統(tǒng)的負(fù)載的,訪問(wèn)量大的時(shí)候還是有引發(fā)雪崩效應(yīng)的風(fēng)險(xiǎn)——如果線程資源都被重試的請(qǐng)求占用了其它請(qǐng)求得不要線程資源。
這個(gè)問(wèn)題可以通過(guò)熔斷、隔離、限流來(lái)解決,這里不再詳述。

確保冪等

在對(duì)某個(gè)操作進(jìn)行重試前,我們應(yīng)該確保該同一操作無(wú)論發(fā)起多少次請(qǐng)求對(duì)數(shù)據(jù)的影響都是一致的,不會(huì)因?yàn)槎啻握?qǐng)求而產(chǎn)生副作用。
一般數(shù)據(jù)查詢類操作是天然冪等的,而數(shù)據(jù)增、刪、改則是非冪等的,需要做好冪等處理。

總結(jié)

重試是一把雙刃劍,它既能幫助我們降低調(diào)用的失敗率來(lái)提高系統(tǒng)的穩(wěn)定性,也能在極端情況下引發(fā)系統(tǒng)雪崩,造成極大的損失。
因此,有些公司禁止程序員使用重試機(jī)制。我們不評(píng)價(jià)其規(guī)定是否合理,但可以看出如果濫用重試確實(shí)會(huì)造成極其負(fù)面的影響。
所以,在微服務(wù)的架構(gòu)中,雖然重試可以有些提高系統(tǒng)的穩(wěn)定性,但也要謹(jǐn)慎的使用重試以免造成不必要的損失。

擴(kuò)展閱讀

架構(gòu)設(shè)計(jì)思維篇之結(jié)構(gòu)

架構(gòu)設(shè)計(jì)思維篇之概念

架構(gòu)設(shè)計(jì)容錯(cuò)篇之重試

架構(gòu)設(shè)計(jì)容錯(cuò)篇之熔斷

架構(gòu)設(shè)計(jì)容錯(cuò)篇之限流

架構(gòu)設(shè)計(jì)事務(wù)篇之Mysql事務(wù)原理

架構(gòu)設(shè)計(jì)事務(wù)篇之CAP定理

架構(gòu)設(shè)計(jì)事務(wù)篇之分布式事務(wù)

架構(gòu)設(shè)計(jì)消息篇之消息丟失

架構(gòu)設(shè)計(jì)消息篇之保證消息順序性

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容