舉個(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 (失?。?br> 3)就是A賬戶(hù)減100(成功),B賬戶(hù)加100 (失?。?br> 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á)到最終的一致性。