94 - 實(shí)戰(zhàn)之通用的接口冪等框架(設(shè)計(jì)篇)

跟限流框架類似,冪等框架的功能性需求也比較簡(jiǎn)單,但要考慮處理的異常情況有很多渡处,比如業(yè)務(wù)代碼異常镜悉、業(yè)務(wù)系統(tǒng)宕機(jī)、冪等框架異常医瘫。本文侣肄,我們重點(diǎn)講解如何應(yīng)對(duì)這些異常情況,設(shè)計(jì)一個(gè)高度容錯(cuò)的冪等框架醇份。

冪等處理正常流程

  • 調(diào)用方從發(fā)起接口請(qǐng)求到接收到響應(yīng)稼锅,一般要經(jīng)過三個(gè)階段。第一個(gè)階段是調(diào)用方發(fā)送請(qǐng)求并被實(shí)現(xiàn)方接收僚纷,第二個(gè)階段是執(zhí)行接口對(duì)應(yīng)的業(yè)務(wù)邏輯矩距,第三個(gè)階段是將執(zhí)行結(jié)果返回給調(diào)用方。為了實(shí)現(xiàn)接口冪等怖竭,我們需要將冪等相關(guān)的邏輯锥债,添加在這三個(gè)階段中。
  • 正常情況下痊臭,冪等號(hào)隨著請(qǐng)求傳遞到接口實(shí)現(xiàn)方之后哮肚,接口實(shí)現(xiàn)方將冪等號(hào)解析出來,傳遞給冪等框架趣兄。冪等框架先去數(shù)據(jù)庫(kù)(比如 Redis)中查找這個(gè)冪等號(hào)是否已經(jīng)存在绽左。如果存在,說明業(yè)務(wù)邏輯已經(jīng)或者正在執(zhí)行艇潭,就不要重復(fù)執(zhí)行了拼窥。如果冪等號(hào)不存在,就將冪等號(hào)存儲(chǔ)在數(shù)據(jù)庫(kù)中蹋凝,然后再執(zhí)行相應(yīng)的業(yè)務(wù)邏輯鲁纠。
  • 正常情況下,冪等處理流程是非常簡(jiǎn)單的鳍寂,難點(diǎn)在于如何應(yīng)對(duì)異常情況改含。在這三個(gè)階段中,如果第一個(gè)階段出現(xiàn)異常迄汛,比如發(fā)送請(qǐng)求失敗或者超時(shí)捍壤,冪等號(hào)還沒有記錄下來,重試請(qǐng)求會(huì)被執(zhí)行鞍爱,符合我們的預(yù)期鹃觉。如果第三個(gè)階段出現(xiàn)異常,業(yè)務(wù)邏輯執(zhí)行完成了睹逃,只是在發(fā)送結(jié)果給調(diào)用方的時(shí)候盗扇,失敗或者超時(shí)了,這個(gè)時(shí)候,冪等號(hào)已經(jīng)記錄下來疗隶,重試請(qǐng)求不會(huì)被執(zhí)行佑笋,也符合我們的預(yù)期。也就是說斑鼻,第一蒋纬、第三階段出現(xiàn)異常,上述的冪等處理邏輯都可以正確應(yīng)對(duì)卵沉。
  • 但是颠锉,如果第二個(gè)階段業(yè)務(wù)執(zhí)行的過程出現(xiàn)異常法牲,處理起來就復(fù)雜多了史汗。接下來,我們就看下冪等框架該如何應(yīng)對(duì)這一階段的各種異常拒垃。這里分了三類異常來講解停撞,它們分別是業(yè)務(wù)代碼異常、業(yè)務(wù)系統(tǒng)宕機(jī)悼瓮、冪等框架異常戈毒。

業(yè)務(wù)代碼異常處理

  • 當(dāng)業(yè)務(wù)代碼在執(zhí)行過程中拋出異常的時(shí)候,我們是否應(yīng)該認(rèn)定為業(yè)務(wù)處理失敗横堡,然后將已經(jīng)記錄的冪等號(hào)刪除埋市,允許重新執(zhí)行業(yè)務(wù)邏輯呢?
  • 對(duì)于這個(gè)問題命贴,我們要分業(yè)務(wù)異常和系統(tǒng)異常來區(qū)分對(duì)待道宅。那什么是業(yè)務(wù)異常?什么是系統(tǒng)異常呢胸蛛?舉個(gè)例子解釋一下污茵。比如,A 用戶發(fā)送消息給 B 用戶葬项,但是查詢 B 用戶不存在泞当,拋出 UserNotExisting 異常,我們把這種業(yè)務(wù)上不符合預(yù)期叫做業(yè)務(wù)異常民珍。因?yàn)閿?shù)據(jù)庫(kù)掛掉了襟士,業(yè)務(wù)代碼訪問數(shù)據(jù)庫(kù)時(shí),就會(huì)報(bào)告數(shù)據(jù)庫(kù)異常嚷量,我們把這種非業(yè)務(wù)層面的陋桂、系統(tǒng)級(jí)的異常,叫做系統(tǒng)異常津肛。
  • 遇到業(yè)務(wù)異常(比如 UserNotExisting 異常)章喉,我們不刪除已經(jīng)記錄的冪等號(hào),不允許重新執(zhí)行同樣的業(yè)務(wù)邏輯,因?yàn)樵俅沃匦聢?zhí)行也是徒勞的秸脱,還是會(huì)報(bào)告異常落包。相反,遇到系統(tǒng)異常(比如數(shù)據(jù)庫(kù)訪問異常)摊唇,我們將已經(jīng)記錄的冪等號(hào)刪除咐蝇,允許重新執(zhí)行這段業(yè)務(wù)邏輯。因?yàn)樵谙到y(tǒng)級(jí)問題修復(fù)之后(比如數(shù)據(jù)庫(kù)恢復(fù)了)巷查,重新執(zhí)行之前失敗的業(yè)務(wù)邏輯有序,就有可能會(huì)成功。
  • 實(shí)際上岛请,為了讓冪等框架盡可能的靈活旭寿,低侵入業(yè)務(wù)邏輯,發(fā)生異常(不管是業(yè)務(wù)異常還是系統(tǒng)異常)崇败,是否允許再重試執(zhí)行業(yè)務(wù)邏輯盅称,交給開發(fā)這塊業(yè)務(wù)的工程師來決定是最合適的了,畢竟他最清楚針對(duì)每個(gè)異常該如何處理后室。而冪等框架本身不參與這個(gè)決定缩膝,它只需要提供刪除冪等號(hào)的接口,由業(yè)務(wù)工程師來決定遇到異常的時(shí)候岸霹,是否需要調(diào)用這個(gè)刪除接口疾层,刪除已經(jīng)記錄的冪等號(hào)。

業(yè)務(wù)系統(tǒng)宕機(jī)處理

  • 剛剛分析的是代碼異常贡避,我們?cè)賮砜聪峦蠢瑁绻跇I(yè)務(wù)處理的過程中,業(yè)務(wù)系統(tǒng)宕機(jī)了(你可以簡(jiǎn)單理解為部署了業(yè)務(wù)系統(tǒng)的機(jī)器宕機(jī)了)贸桶,冪等框架是否還能正確工作呢舅逸?
  • 如果冪等號(hào)已經(jīng)記錄下了,但是因?yàn)闄C(jī)器宕機(jī)皇筛,業(yè)務(wù)還沒來得及執(zhí)行琉历,按照剛剛的冪等框架的處理流程,即便機(jī)器重啟水醋,業(yè)務(wù)也不會(huì)再被觸發(fā)執(zhí)行了旗笔,這個(gè)時(shí)候該怎么辦呢?除此之外拄踪,如果記錄冪等號(hào)成功了蝇恶,但是在捕獲到系統(tǒng)異常之后,要?jiǎng)h除冪等號(hào)之前惶桐,機(jī)器宕機(jī)了撮弧,這個(gè)時(shí)候又該怎么辦潘懊?
  • 如果希望冪等號(hào)的記錄和業(yè)務(wù)的執(zhí)行完全一致,我們就要把它們放到一個(gè)事務(wù)中贿衍。執(zhí)行成功授舟,必然會(huì)記錄冪等號(hào);執(zhí)行失敗贸辈,冪等號(hào)記錄也會(huì)被自動(dòng)回滾释树。因?yàn)閮绲瓤蚣芎蜆I(yè)務(wù)系統(tǒng)各自使用獨(dú)立的數(shù)據(jù)庫(kù)來記錄數(shù)據(jù),所以擎淤,這里涉及的事務(wù)屬于分布式事務(wù)奢啥。如果為了解決這個(gè)問題,引入分布式事務(wù)嘴拢,那冪等框架的開發(fā)難度提高了很多桩盲,并且框架使用起來也復(fù)雜了很多,性能也會(huì)有所損失炊汤。
  • 針對(duì)這個(gè)問題正驻,我們還有另外一種解決方案。那就是抢腐,在存儲(chǔ)業(yè)務(wù)數(shù)據(jù)的業(yè)務(wù)數(shù)據(jù)庫(kù)( 比如 MySQL)中,建一張表來記錄冪等號(hào)襟交。冪等號(hào)先存儲(chǔ)到業(yè)務(wù)數(shù)據(jù)庫(kù)中迈倍,然后再同步給冪等框架的 Redis 數(shù)據(jù)庫(kù)。這樣做的好處是捣域,我們不需要引入分布式事務(wù)框架啼染,直接利用業(yè)務(wù)數(shù)據(jù)庫(kù)本身的事務(wù)屬性,保證業(yè)務(wù)數(shù)據(jù)和冪等號(hào)的寫入操作焕梅,要么都成功迹鹅,要么都失敗。不過贞言,這個(gè)解決方案會(huì)導(dǎo)致冪等邏輯斜棚,跟業(yè)務(wù)邏輯沒有完全解耦,不符合我們之前講到的低侵入该窗、松耦合的設(shè)計(jì)思想弟蚀。
  • 實(shí)際上,做工程不是做理論酗失。對(duì)于這種極少發(fā)生的異常义钉,在工程中,我們能夠做到规肴,在出錯(cuò)時(shí)能及時(shí)發(fā)現(xiàn)問題捶闸、能夠根據(jù)記錄的信息人工修復(fù)就可以了夜畴。雖然看起來解決方案不優(yōu)雅,不夠智能删壮,不夠自動(dòng)化斩启,但是,這比編寫一大坨復(fù)雜的代碼邏輯來解決醉锅,要好使得多兔簇。所以,我們建議業(yè)務(wù)系統(tǒng)記錄 SQL 的執(zhí)行日志硬耍,在日志中附加上冪等號(hào)垄琐。這樣我們就能在機(jī)器宕機(jī)時(shí),根據(jù)日志來判斷業(yè)務(wù)執(zhí)行情況和冪等號(hào)的記錄是否一致经柴。

冪等框架異常處理

  • 我們前面提到狸窘,限流框架本身的異常,不能導(dǎo)致接口響應(yīng)異常坯认。那對(duì)于冪等框架來說翻擒,是否也適用這條設(shè)計(jì)原則呢?
  • 對(duì)于限流來說牛哺,限流框架執(zhí)行異常(比如陋气,Redis 訪問超時(shí)或者訪問失敗)引润,我們可以觸發(fā)服務(wù)降級(jí)巩趁,讓限流功能暫時(shí)不起作用,接口還能正常執(zhí)行淳附。如果大量的限流接口調(diào)用異常议慰,在具有完善監(jiān)控的情況下,這些異常很快就會(huì)被運(yùn)維發(fā)現(xiàn)并且修復(fù)奴曙,所以别凹,短暫的限流失效,也不會(huì)對(duì)業(yè)務(wù)系統(tǒng)產(chǎn)生太多影響洽糟。畢竟限流只是一個(gè)針對(duì)突發(fā)情況的保護(hù)機(jī)制炉菲,平時(shí)并不起作用。如果偶爾的極個(gè)別的限流接口調(diào)用異常脊框,本不應(yīng)該被放過的幾個(gè)接口請(qǐng)求颁督,因?yàn)橄蘖鞯臅簳r(shí)失效被放過了,對(duì)于這種情況浇雹,絕大部分業(yè)務(wù)場(chǎng)景都是可以接受的沉御。畢竟限流不可能做到非常精確,多放過一兩個(gè)接口請(qǐng)求幾乎沒影響昭灵。
  • 對(duì)于冪等來說吠裆,盡管它應(yīng)對(duì)的也是超時(shí)重試等特殊場(chǎng)景伐谈,但是,如果本不應(yīng)該重新執(zhí)行的業(yè)務(wù)邏輯试疙,因?yàn)閮绲裙δ艿臅簳r(shí)失效诵棵,被重復(fù)執(zhí)行了,就會(huì)導(dǎo)致業(yè)務(wù)出錯(cuò)(比如祝旷,多次執(zhí)行轉(zhuǎn)賬履澳,錢多轉(zhuǎn)了)。對(duì)于這種情況怀跛,絕大部分業(yè)務(wù)場(chǎng)景都是無法接受的距贷。所以,在冪等邏輯執(zhí)行異常時(shí)吻谋,我們選擇讓接口請(qǐng)求也失敗忠蝗,相應(yīng)的業(yè)務(wù)邏輯就不會(huì)被重復(fù)執(zhí)行了。畢竟接口請(qǐng)求失斃焓啊(比如轉(zhuǎn)錢沒轉(zhuǎn)成功)阁最,比業(yè)務(wù)執(zhí)行出錯(cuò)(比如多轉(zhuǎn)了錢),修復(fù)的成本要低很多骇两。

小結(jié)

  • 本文分了三類異常來對(duì)冪等框架進(jìn)行講解
    • 對(duì)于業(yè)務(wù)代碼異常速种,為了讓冪等框架盡可能的靈活,低侵入業(yè)務(wù)邏輯脯颜,發(fā)生異常(不管是業(yè)務(wù)異常還是系統(tǒng)異常)哟旗,是否允許再重試執(zhí)行業(yè)務(wù)邏輯,交給開發(fā)這塊業(yè)務(wù)的工程師來決定栋操。
    • 對(duì)于業(yè)務(wù)系統(tǒng)宕機(jī),對(duì)于這種極少發(fā)生的異常饱亮,在工程中矾芙,我們能夠做到,在出錯(cuò)時(shí)能及時(shí)發(fā)現(xiàn)問題近上、能夠根據(jù)記錄的信息人工修復(fù)剔宪,就可以了。所以壹无,我們建議業(yè)務(wù)系統(tǒng)記錄 SQL 的執(zhí)行日志葱绒,在日志中附加上冪等號(hào)。這樣我們就能在機(jī)器宕機(jī)時(shí)斗锭,根據(jù)日志來判斷業(yè)務(wù)執(zhí)行情況和冪等號(hào)的記錄是否一致地淀。
    • 對(duì)于冪等框架異常,跟限流框架異常處理對(duì)策不同岖是,在冪等邏輯執(zhí)行異常時(shí)帮毁,我們選擇讓接口請(qǐng)求也失敗实苞,相應(yīng)的業(yè)務(wù)邏輯就不會(huì)被重復(fù)執(zhí)行了,業(yè)務(wù)就不會(huì)出錯(cuò)烈疚。畢竟接口請(qǐng)求失敗黔牵,比業(yè)務(wù)執(zhí)行出錯(cuò),修復(fù)的成本要低很多爷肝。
  • 雖然冪等框架要處理的異常很多猾浦,但考慮到開發(fā)成本以及簡(jiǎn)單易用性,我們對(duì)某些異常的處理在工程上做了妥協(xié)灯抛,交由業(yè)務(wù)系統(tǒng)或者人工介入處理金赦。這樣就大大簡(jiǎn)化了冪等框架開發(fā)的復(fù)雜度和難度。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牧愁,一起剝皮案震驚了整個(gè)濱河市素邪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猪半,老刑警劉巖兔朦,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異磨确,居然都是意外死亡沽甥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門乏奥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摆舟,“玉大人,你說我怎么就攤上這事邓了『抻眨” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵骗炉,是天一觀的道長(zhǎng)照宝。 經(jīng)常有香客問我,道長(zhǎng)句葵,這世上最難降的妖魔是什么厕鹃? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮乍丈,結(jié)果婚禮上剂碴,老公的妹妹穿的比我還像新娘。我一直安慰自己轻专,他們只是感情好忆矛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铭若,像睡著了一般洪碳。 火紅的嫁衣襯著肌膚如雪递览。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天瞳腌,我揣著相機(jī)與錄音绞铃,去河邊找鬼。 笑死嫂侍,一個(gè)胖子當(dāng)著我的面吹牛儿捧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挑宠,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼菲盾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了各淀?” 一聲冷哼從身側(cè)響起懒鉴,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碎浇,沒想到半個(gè)月后临谱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奴璃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年悉默,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苟穆。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抄课,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雳旅,到底是詐尸還是另有隱情跟磨,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布攒盈,位于F島的核電站吱晒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沦童。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一叹话、第九天 我趴在偏房一處隱蔽的房頂上張望偷遗。 院中可真熱鬧,春花似錦驼壶、人聲如沸氏豌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泵喘。三九已至泪电,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纪铺,已是汗流浹背相速。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鲜锚,地道東北人突诬。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像芜繁,于是被迫代替她去往敵國(guó)和親旺隙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容