新聞中心
為什么微服務(wù)重試機(jī)制很重要?
當(dāng)我們單體應(yīng)用時(shí),所有的邏輯計(jì)算都在單一的進(jìn)程中,除了進(jìn)程斷電外幾乎不可能有處理失敗的情況。然而,當(dāng)我們把單體應(yīng)用拆分為一個(gè)個(gè)細(xì)分的子服務(wù)后,服務(wù)間的互相調(diào)用無(wú)論是RPC還是HTTP,都是依賴于網(wǎng)絡(luò)。

成都創(chuàng)新互聯(lián)公司主營(yíng)樂(lè)東黎族網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,成都App制作,樂(lè)東黎族h5成都小程序開發(fā)搭建,樂(lè)東黎族網(wǎng)站營(yíng)銷推廣歡迎樂(lè)東黎族等地區(qū)企業(yè)咨詢
網(wǎng)絡(luò)是脆弱的,不時(shí)請(qǐng)求會(huì)出現(xiàn)抖動(dòng)失敗。例如我們的 Server1 調(diào)用 Server2 進(jìn)行下單時(shí),可能網(wǎng)絡(luò)超時(shí)了,這個(gè)時(shí)候 Server1 就需要返回給用戶提示「網(wǎng)絡(luò)錯(cuò)誤」,這樣我們的服務(wù)質(zhì)量就下降了,可能會(huì)收到用戶的投訴吐槽,降低產(chǎn)品競(jìng)爭(zhēng)力。
這也是為什么很多產(chǎn)品內(nèi)部都建設(shè)接口維度的 SLA 指標(biāo),當(dāng)成功率低于一定程度時(shí)需要和負(fù)責(zé)人績(jī)效掛鉤以此來(lái)推進(jìn)產(chǎn)品的穩(wěn)定性。
對(duì)于網(wǎng)絡(luò)抖動(dòng)這種情況,解決的最簡(jiǎn)單辦法之一就是重試。
重試機(jī)制
重試機(jī)制:同步 、異步模式
常見(jiàn)的重試主要有兩種模式:原地重試、異步重試。
原地重試很好理解,就是程序在調(diào)用下游服務(wù)失敗的時(shí)候重新發(fā)起一次;異步重試是將請(qǐng)求信息丟到某個(gè) mq 中,后續(xù)有一個(gè)程序消費(fèi)到這個(gè)事件進(jìn)行重試。
總的來(lái)說(shuō),原地重試實(shí)現(xiàn)簡(jiǎn)單,能解決大部分網(wǎng)絡(luò)抖動(dòng)問(wèn)題,但是如果是服務(wù)追求強(qiáng)一致性,并且希望在下游故障的時(shí)候不影響正常服務(wù)計(jì)算,這個(gè)時(shí)候可以考慮用異步重試,上游服務(wù)可快速響應(yīng)用戶請(qǐng)求由異步消費(fèi)者去完成重試。
重試算法
無(wú)論是異步還是同步模式,重試都有固定的幾個(gè)算法:
- 線性退避:每次失敗固定等待固定的時(shí)間。
- 隨機(jī)退避:每次失敗等待隨機(jī)的時(shí)間重試。
- 指數(shù)退避:連續(xù)重試時(shí),每次等待的時(shí)間都是前一次等待時(shí)間的倍數(shù)。
- 綜合退避:結(jié)合多種方式,比如線性 + 隨機(jī)抖動(dòng)、指數(shù) + 隨機(jī)抖動(dòng)。加上隨機(jī)抖動(dòng)可以打散眾多服務(wù)失敗時(shí)對(duì)下游的重試請(qǐng)求,防止雪崩。
為什么需要等待下再重試?
因?yàn)榫W(wǎng)絡(luò)抖動(dòng)或者下游負(fù)載高,馬上重試成功的概率必然遠(yuǎn)遠(yuǎn)小于稍等一會(huì)再重試,相當(dāng)于是讓下游先喘一口氣。
重試風(fēng)暴
在微服務(wù)架構(gòu)中,務(wù)必要注意避免重試風(fēng)暴的產(chǎn)生。那么,什么是重試風(fēng)暴呢?
如圖所示,數(shù)據(jù)庫(kù)出現(xiàn)了負(fù)載過(guò)高的情況,這個(gè)時(shí)候 Server 3 對(duì)它的請(qǐng)求會(huì)失敗。但是因?yàn)榕渲昧酥卦嚈C(jī)制,Server 3 最多對(duì)數(shù)據(jù)庫(kù)發(fā)起了3次請(qǐng)求。然而,這個(gè)時(shí)候荒唐的事情就出現(xiàn)了,為了避免抖動(dòng)上游的每個(gè)服務(wù)都設(shè)置了超時(shí)重試3次的機(jī)制,這樣明明是一次業(yè)務(wù)請(qǐng)求,在上述中由于有3個(gè)環(huán)節(jié)存在變成了對(duì)數(shù)據(jù)庫(kù)的 27 (3 ^(n)) 次請(qǐng)求!這對(duì)原本就要崩潰的數(shù)據(jù)庫(kù),更是雪上加霜。
微服務(wù)架構(gòu)通常一次請(qǐng)求會(huì)經(jīng)過(guò)數(shù)個(gè)甚至數(shù)百個(gè)服務(wù)處理,如果每個(gè)都這樣重試,數(shù)據(jù)庫(kù)壓力稍微彪高一點(diǎn)本身沒(méi)啥問(wèn)題,但是很可能就因?yàn)橹卦噷?dǎo)致雪崩。
如何防止重試風(fēng)暴
單實(shí)例限流
首先,我們接受請(qǐng)求的是單個(gè)實(shí)例(進(jìn)程)中的線程,所以可以以單進(jìn)程的粒度進(jìn)行限流。
關(guān)于限流,我們常用的是令牌桶或者滑動(dòng)窗口兩種實(shí)現(xiàn),這里簡(jiǎn)單實(shí)用滑動(dòng)窗口實(shí)現(xiàn)。如下圖所示,每秒會(huì)產(chǎn)生一個(gè)Bucket,我們?cè)贐ucket里記錄這一秒內(nèi)對(duì)下游某個(gè)接口的成功、失敗數(shù)量。進(jìn)而可以統(tǒng)計(jì)出每秒的失敗率,結(jié)合失敗率及失敗請(qǐng)求數(shù)判斷是否需要重試,每個(gè) Bucket 在一定時(shí)間后過(guò)期。
如果下游大面積失敗,這種時(shí)候是不適合重試的,我們可以配置一個(gè)比如失敗率超過(guò)10%不重試的策略,這樣在單機(jī)層面就可以避免很多不必要的重試。
規(guī)范重試狀態(tài)碼
鏈路層面防止重試的最好做法是只在最下游重試(我們上面圖的 Server3),Google SRE中指出了Google內(nèi)部使用特殊錯(cuò)誤碼的方式來(lái)實(shí)現(xiàn):
- 約定一個(gè)特殊的業(yè)務(wù)狀態(tài)碼,它表示失敗了,但是別重試。
- 任何一個(gè)環(huán)節(jié)收到下游這個(gè)錯(cuò)誤,不會(huì)重試,繼續(xù)透?jìng)鹘o上游。
通過(guò)這個(gè)模式,如果是數(shù)據(jù)庫(kù)抖動(dòng)情況下,只有最下游的三個(gè)重試請(qǐng)求,上游服務(wù)判斷狀態(tài)碼知道不可重試不再重試。除此之外,在一些業(yè)務(wù)異常情況下也可通過(guò)狀態(tài)碼區(qū)分出無(wú)需重試的狀態(tài)。
這個(gè)方法可以有效避免重試風(fēng)暴,但是缺陷是需要業(yè)務(wù)方強(qiáng)耦合上這個(gè)狀態(tài)碼的邏輯,一般需要公司層面做框架上的約束。
超時(shí)優(yōu)化
在重試中,最頭疼的莫過(guò)于超時(shí)這種場(chǎng)景。我們知道網(wǎng)絡(luò)超時(shí),有可能請(qǐng)求壓根沒(méi)到下游服務(wù)就產(chǎn)生了,也可能是已經(jīng)到達(dá)下游并且被處理了,只是來(lái)不及返回,一個(gè)典型的兩軍問(wèn)題。
關(guān)于超時(shí)的情況,顯然無(wú)法通過(guò)錯(cuò)誤碼識(shí)別,例如 A -> B -> C -> D 情況,如果C故障了,B可以獲取到錯(cuò)誤碼,并返回給 A,但是因?yàn)?A 請(qǐng)求 B 超時(shí)了,所以是獲取不到錯(cuò)誤碼的,這個(gè)時(shí)候 A 又會(huì)發(fā)起重試。那么針對(duì)超時(shí)的情況有沒(méi)什么辦法做優(yōu)化,避免無(wú)必要的重試呢?
我認(rèn)為有幾個(gè)地方是可以做的:
上游重試的請(qǐng)求不重試
超時(shí)導(dǎo)致的重試請(qǐng)求,在請(qǐng)求中帶一個(gè) Flag 標(biāo)記。如果下游發(fā)現(xiàn)上游是因?yàn)槌瑫r(shí)而發(fā)起的請(qǐng)求,自己在請(qǐng)求下游時(shí)如果再超時(shí)出錯(cuò),不再重試。例如 A -> B -> C 時(shí),A 請(qǐng)求 B 超時(shí)重試,那么重試時(shí)會(huì)帶上 Flag,B 發(fā)現(xiàn) A 的重試請(qǐng)求中的 Flag,如果這個(gè)時(shí)候請(qǐng)求 C 失敗,那么也不再重試請(qǐng)求,這樣就避免了重試被放大。
合理設(shè)置各個(gè)環(huán)節(jié)超時(shí)時(shí)間
A -> B -> C,B -> C 加上超時(shí)最多是 1s 時(shí)間,那么 A -> B 的超時(shí)時(shí)間要 >= 1秒,否則可能 B 對(duì) C 的重試還沒(méi)結(jié)束, A 就發(fā)起重試請(qǐng)求了。這類問(wèn)題,我們可以通過(guò)分析離線數(shù)據(jù)發(fā)現(xiàn)環(huán)節(jié)中存在的不合理配置。
通過(guò)上述的優(yōu)化,我們可以在一定程度上規(guī)避超時(shí)引發(fā)的重試風(fēng)暴。
降低時(shí)延的重試
我們上文主要都在闡述為了保障請(qǐng)求 SLA 的重試以及規(guī)避重試風(fēng)暴的手段,但是其實(shí)在實(shí)際應(yīng)用過(guò)程中有一些低時(shí)延的業(yè)務(wù)場(chǎng)景也經(jīng)常使用重試來(lái)優(yōu)化,這個(gè)優(yōu)化措施就是 backupRequest。
比方說(shuō)用戶下單接口,我們希望更低的時(shí)延,因?yàn)檠舆t變高了用戶可能下單量就減少了,直接影響到公司的盈利。假設(shè)我們的接口時(shí)延 p95 是 300ms,也就是95%的用戶能在 300ms 內(nèi)完成下單,雖然看起來(lái)很美好,但是可能存在 “長(zhǎng)尾效應(yīng)”,這尾部的 5% 對(duì)于業(yè)務(wù)來(lái)說(shuō)也是至關(guān)重要的。
對(duì)于這種情況,常見(jiàn)的優(yōu)化方案就是 backupRequest,簡(jiǎn)單來(lái)說(shuō)策略就是這樣的:
如果正常請(qǐng)求的超時(shí)時(shí)間是1s,那么當(dāng)超時(shí)時(shí)間超過(guò)x ms(eg. 500ms)不等超時(shí)時(shí)間直接再發(fā)起一個(gè)相同的請(qǐng)求,如果舊的請(qǐng)求超時(shí),新的請(qǐng)求正常落在300ms以內(nèi),那么我們這次請(qǐng)求不會(huì)超時(shí)且會(huì)在超時(shí)時(shí)間內(nèi)完成。
這個(gè)機(jī)制對(duì)于時(shí)延敏感的業(yè)務(wù)非常有效,但是必須要保證請(qǐng)求是可重試的。
總結(jié)
這篇文章到這里就接近尾聲了,如果你堅(jiān)持讀到這里,恭喜你已經(jīng)掌握了微服務(wù)的重試機(jī)制,相信在工作中遇到的問(wèn)題也都能游刃有余。下面我簡(jiǎn)單做下總結(jié):
- 微服務(wù)重試很重要,因?yàn)榭梢员苊庖恍┚W(wǎng)絡(luò)波動(dòng)導(dǎo)致的請(qǐng)求失敗,提升服務(wù)穩(wěn)定性。
- 重試機(jī)制分為同步、異步兩種模式,各有各的特性,需要結(jié)合業(yè)務(wù)選擇。
- 常見(jiàn)的重試算法有線性退避、指數(shù)退避、隨機(jī)退避,以及結(jié)合其中兩種的綜合退避。
- 重試風(fēng)暴,在微服務(wù)中是一大隱患,我們可以通過(guò)單機(jī)重試限流以及約定重試狀態(tài)碼來(lái)規(guī)避。
- 超時(shí)場(chǎng)景下的重試優(yōu)化,上游因超時(shí)發(fā)起的流量,下游收到不再重復(fù)重試;合理配置鏈路超時(shí)時(shí)間。
- 針對(duì)時(shí)延敏感業(yè)務(wù),可使用 backup request 減輕長(zhǎng)尾效應(yīng)。
網(wǎng)頁(yè)標(biāo)題:系統(tǒng)設(shè)計(jì):微服務(wù)重試機(jī)制
URL分享:http://www.fisionsoft.com.cn/article/dppeiss.html


咨詢
建站咨詢
