舉個(gè)分布式事務(wù)場(chǎng)景
列子:假設(shè) A 給 B 轉(zhuǎn) 100塊錢(qián)饭聚,同時(shí)它們不是同一個(gè)服務(wù)上糖权。
目標(biāo):就是 A 減100塊錢(qián)举畸,B 加100塊錢(qián)箕别。
實(shí)際情況可能有四種:
1)就是A賬戶(hù)減100 (成功),B賬戶(hù)加100 (成功)
2)就是A賬戶(hù)減100(失斉砦怼)碟刺,B賬戶(hù)加100 (失敗)
3)就是A賬戶(hù)減100(成功)薯酝,B賬戶(hù)加100 (失敯牍痢)
4)就是A賬戶(hù)減100 (失敗)吴菠,B賬戶(hù)加100 (成功)
這里 第1 和 第2 種情況是能夠保證事務(wù)的一致性的者填,但是 第3 和 第4 是無(wú)法保證事務(wù)的一致性的。
那我們來(lái)看下RocketMQ是如何來(lái)保證事務(wù)的一致性的
RocketMQ實(shí)現(xiàn)分布式事務(wù)原理
1做葵、基礎(chǔ)概念
-
最終一致性
RocketMQ是一種最終一致性的分布式事務(wù)占哟,就是說(shuō)它保證的是消息最終一致性,而不是像2PC酿矢、3PC榨乎、TCC那樣強(qiáng)一致分布式事務(wù),至于為什么說(shuō)它是最終一致性事務(wù)下面會(huì)詳細(xì)說(shuō)明棠涮。
-
Half Message(半消息)
是指暫不能被Consumer消費(fèi)的消息谬哀。Producer 已經(jīng)把消息成功發(fā)送到了 Broker 端刺覆,但此消息被標(biāo)記為暫不能投遞狀態(tài)严肪,處于該種狀態(tài)下的消息稱(chēng)為半消息。需要 Producer對(duì)消息的二次確認(rèn)后谦屑,Consumer才能去消費(fèi)它驳糯。
-
消息回查
由于網(wǎng)絡(luò)閃段,生產(chǎn)者應(yīng)用重啟等原因氢橙。導(dǎo)致 Producer 端一直沒(méi)有對(duì) Half Message(半消息) 進(jìn)行 二次確認(rèn)酝枢。這是Brock服務(wù)器會(huì)定時(shí)掃描長(zhǎng)期處于半消息的消息,會(huì)主動(dòng)詢(xún)問(wèn)Producer端 該消息的最終狀態(tài)(Commit或者Rollback),該消息即為 消息回查悍手。
2帘睦、分布式事務(wù)交互流程
理解這張阿里官方的圖,就能理解RocketMQ分布式事務(wù)的原理了坦康。
說(shuō)明
1竣付、A服務(wù)先發(fā)送個(gè)Half Message給Brock端,消息中攜帶 B服務(wù) 即將要+100元的信息滞欠。
2古胆、當(dāng)A服務(wù)知道Half Message發(fā)送成功后,那么開(kāi)始第3步執(zhí)行本地事務(wù)。
3逸绎、執(zhí)行本地事務(wù)(會(huì)有三種情況1惹恃、執(zhí)行成功。2棺牧、執(zhí)行失敗巫糙。3、網(wǎng)絡(luò)等原因?qū)е聸](méi)有響應(yīng))
4.1)颊乘、如果本地事務(wù)成功曲秉,那么Product像Brock服務(wù)器發(fā)送Commit,這樣B服務(wù)就可以消費(fèi)該message。
4.2)疲牵、如果本地事務(wù)失敗承二,那么Product像Brock服務(wù)器發(fā)送Rollback,那么就會(huì)直接刪除上面這條半消息。
4.3)纲爸、如果因?yàn)榫W(wǎng)絡(luò)等原因遲遲沒(méi)有返回失敗還是成功亥鸠,那么會(huì)執(zhí)行RocketMQ的回調(diào)接口,來(lái)進(jìn)行事務(wù)的回查。
從上面流程可以得知 只有A服務(wù)本地事務(wù)執(zhí)行成功 识啦,B服務(wù)才能消費(fèi)該message负蚊。
然后我們?cè)賮?lái)思考幾個(gè)問(wèn)題?
為什么要先發(fā)送Half Message(半消息)
我覺(jué)得主要有兩點(diǎn):
1)可以先確認(rèn) Brock服務(wù)器是否正常 颓哮,如果半消息都發(fā)送失敗了 那說(shuō)明Brock掛了家妆。
2)可以通過(guò)半消息來(lái)回查事務(wù),如果半消息發(fā)送成功后一直沒(méi)有被二次確認(rèn)冕茅,那么就會(huì)回查事務(wù)狀態(tài)伤极。
什么情況會(huì)回查
也會(huì)有兩種情況:
1)執(zhí)行本地事務(wù)的時(shí)候,由于突然網(wǎng)絡(luò)等原因一直沒(méi)有返回執(zhí)行事務(wù)的結(jié)果(commit或者rollback)導(dǎo)致最終返回UNKNOW姨伤,那么就會(huì)回查哨坪。
2) 本地事務(wù)執(zhí)行成功后,返回Commit進(jìn)行消息二次確認(rèn)的時(shí)候的服務(wù)掛了乍楚,在重啟服務(wù)那么這個(gè)時(shí)候在brock端
它還是個(gè)Half Message(半消息)当编,這也會(huì)回查。
特別注意: 如果回查徒溪,那么一定要先查看當(dāng)前事務(wù)的執(zhí)行情況忿偷,再看是否需要重新執(zhí)行本地事務(wù)。
想象下如果出現(xiàn)第二種情況而引起的回查臊泌,如果不先查看當(dāng)前事務(wù)的執(zhí)行情況鲤桥,而是直接執(zhí)行事務(wù),那么就相當(dāng)于成功執(zhí)行了兩個(gè)本地事務(wù)缺虐。
為什么說(shuō)MQ是最終一致性事務(wù)
通過(guò)上面這幅圖芜壁,我們可以看出,在上面舉例事務(wù)不一致的兩種情況中,永遠(yuǎn)不會(huì)發(fā)生
A賬戶(hù)減100 (失敾弁)顷牌,B賬戶(hù)加100 (成功)
因?yàn)?/strong>:如果A服務(wù)本地事務(wù)都失敗了,那B服務(wù)永遠(yuǎn)不會(huì)執(zhí)行任何操作塞淹,因?yàn)橄焊筒粫?huì)傳到B服務(wù)窟蓝。
那么 A賬戶(hù)減100 (成功),B賬戶(hù)加100 (失敱テ铡) 會(huì)不會(huì)可能存在的运挫。
答案是會(huì)的
因?yàn)锳服務(wù)只負(fù)責(zé)當(dāng)我消息執(zhí)行成功了,保證消息能夠送達(dá)到B,至于B服務(wù)接到消息后最終執(zhí)行結(jié)果A并不管套耕。
那B服務(wù)失敗怎么辦谁帕?
如果B最終執(zhí)行失敗,幾乎可以斷定就是代碼有問(wèn)題所以才引起的異常冯袍,因?yàn)橄M(fèi)端RocketMQ有重試機(jī)制匈挖,如果不是代碼問(wèn)題一般重試幾次就能成功。
如果是代碼的原因引起多次重試失敗后康愤,也沒(méi)有關(guān)系儡循,將該異常記錄下來(lái),由人工處理征冷,人工兜底處理后择膝,就可以讓事務(wù)達(dá)到最終的一致性。