前面我們講了分布式事務(wù)的2PC、3PC , TCC 的原理。這些事務(wù)其實(shí)都在盡力的模擬數(shù)據(jù)庫(kù)的事務(wù),我們可以簡(jiǎn)單的認(rèn)為他們是一個(gè)同步行的事務(wù)。特別是 2PC,3PC 他們完全利用數(shù)據(jù)庫(kù)的事務(wù)能力,在一階段開始事務(wù)后不進(jìn)提交會(huì)嚴(yán)重影響應(yīng)用程序的并發(fā)性能。TCC 一階段雖然不會(huì)阻塞數(shù)據(jù)庫(kù),但是它同樣是在盡力追求同時(shí)成功同時(shí)失敗的一致性要求。但是在很多時(shí)候,我們的應(yīng)用程序的核心業(yè)務(wù)為了追求更高的性能、更高的可用性,可以允許在一段時(shí)間內(nèi)的數(shù)據(jù)不一致性,只需要在最終時(shí)刻數(shù)據(jù)是一致就可以了?;谝陨蠄?chǎng)景我們可以采用基于可靠消息服務(wù)的最終一致性分布式事務(wù)處理方案。
總體架構(gòu)
可靠消息最終一致性的架構(gòu)上分3個(gè)部分:
- 主動(dòng)方:主動(dòng)發(fā)起事務(wù)的一方,一般是指分布式事務(wù)中最先開始執(zhí)行的那個(gè)服務(wù),也是核心業(yè)務(wù)服務(wù)
- 可靠消息服務(wù):可靠消息服務(wù)由關(guān)系型數(shù)據(jù)庫(kù)、消息隊(duì)列MQ等組件組成,利用關(guān)系型數(shù)據(jù)庫(kù)的強(qiáng)一致性特性來(lái)持久化消息的狀態(tài),利用MQ來(lái)保證消息的可靠投遞及消費(fèi)
- 被動(dòng)方:被動(dòng)方訂閱MQ的消息,當(dāng)收到MQ的消息后執(zhí)行對(duì)應(yīng)的業(yè)務(wù)
以上是比較粗狂的結(jié)構(gòu)圖,下面我們來(lái)詳細(xì)分析一下這個(gè)事務(wù)的執(zhí)行過(guò)程。
流程
該方案總體流程上可分為以下步驟:
- 主動(dòng)方在真正的業(yè)務(wù)開始前先向可靠消息服務(wù)發(fā)送一個(gè)“待確認(rèn)”的消息
- 可靠消息服務(wù)收到待確認(rèn)消息后持久化消息到數(shù)據(jù)庫(kù)
- 如果以上操作成功則主動(dòng)方開始真正的業(yè)務(wù),如果失敗則直接放棄執(zhí)行業(yè)務(wù)
- 如果業(yè)務(wù)執(zhí)行成功則發(fā)送“確認(rèn)”消息給可靠消息服務(wù),如果執(zhí)行失敗則發(fā)送“取消”給可靠消息服務(wù)。
- 如果可靠消息服務(wù)收到“確認(rèn)”消息則更新數(shù)據(jù)庫(kù)里的消息記錄的狀態(tài)為“待發(fā)送”,如果收到的消息為“取消”則更新消息狀態(tài)為“已取消”
- 如果上一步更新的數(shù)據(jù)庫(kù)為“待發(fā)送”,那么會(huì)開始往MQ投遞消息,并且更改數(shù)據(jù)庫(kù)里的消息記錄的狀態(tài)為“已發(fā)送”
- 上一步往MQ投遞消息成功后,MQ會(huì)給被動(dòng)方推送消息。
- 被動(dòng)方收到消息后開始處理業(yè)務(wù)
- 如果業(yè)務(wù)處理成功,則被動(dòng)方對(duì)MQ進(jìn)行ACK回復(fù),則這條消息會(huì)從MQ內(nèi)移除掉
- 如果業(yè)務(wù)處理成功,則發(fā)送“已完成”消息給可靠消息服務(wù)
- 可靠消息服務(wù)收到“已完成”消息后更新數(shù)據(jù)庫(kù)消息記錄未“已完成”
異常處理
以上我們描述的是一套在理想情況下執(zhí)行的邏輯。但是分布式系統(tǒng)由于網(wǎng)絡(luò)的存在,網(wǎng)絡(luò)的不可靠性會(huì)導(dǎo)致我們消息的傳遞沒辦法100%成功。我們的可靠消息服務(wù)跟主動(dòng)方、被動(dòng)方之間的交互也是分布式的,這就需要我們?cè)诹鞒躺嫌泻芏嘌a(bǔ)償?shù)臋C(jī)制。以下我們來(lái)討論一些異常情況:
- 如果步驟1發(fā)送“待確認(rèn)”消息失敗,主動(dòng)方業(yè)務(wù)不會(huì)執(zhí)行,直接放棄事務(wù),不會(huì)有影響
- 如果步驟1發(fā)送“待確認(rèn)”消息成功,并且可靠消息已經(jīng)更新“待確認(rèn)”成功,但是由于網(wǎng)絡(luò)問(wèn)題,比如超時(shí),主動(dòng)方得到的結(jié)果是失敗,主動(dòng)方會(huì)放棄執(zhí)行事務(wù),標(biāo)記為“已取消”。這個(gè)時(shí)候就會(huì)出現(xiàn)可靠消息服務(wù)跟主動(dòng)方的狀態(tài)出現(xiàn)不一致的情況。
為解決這個(gè)問(wèn)題,我需要主動(dòng)方提供一個(gè)事務(wù)狀態(tài)查詢接口,可靠消息服務(wù)這邊則啟動(dòng)一個(gè)定時(shí)任務(wù),定時(shí)去查這些長(zhǎng)時(shí)間處于待確認(rèn)的事務(wù),然后通過(guò)主動(dòng)方的接口確認(rèn)這些事務(wù)是已執(zhí)行,還是已取消。 - 如果步驟4,主動(dòng)方發(fā)送“確認(rèn)”消息失敗,可靠消息服務(wù)會(huì)通過(guò)定時(shí)任務(wù)通過(guò)主動(dòng)方的查詢接口去確認(rèn)狀態(tài)。
- 步驟6,投遞消息給MQ跟更新狀態(tài)為“已發(fā)送”,是這個(gè)流程中至關(guān)重要的一步。理想的流程是整個(gè)2個(gè)操作同時(shí)成功同時(shí)失敗,保持強(qiáng)一致性。網(wǎng)上很多文章都會(huì)說(shuō)“為了保證發(fā)送MQ消息跟更新消息狀態(tài)同時(shí)成功同時(shí)失敗,需要把這個(gè)2個(gè)步驟寫同一個(gè)本地事務(wù)中”。這是完全不靠譜的,數(shù)據(jù)庫(kù)跟MQ本就是2個(gè)獨(dú)立的服務(wù),如果通過(guò)一個(gè)本地的事務(wù)就能保證一致性,那么我們現(xiàn)在討論的分布式事務(wù)毫無(wú)意義,直接寫在一個(gè)本地事務(wù)里不就完了么。
寫在本地事務(wù)內(nèi)只能盡可能的保證數(shù)據(jù)庫(kù)更新跟MQ投遞消息同時(shí)成功,但是并不能保證100%一致。
以下我們來(lái)分2種情況分析失敗的情況:
事務(wù)開始
1. send to mq
2. database update
事務(wù)結(jié)束
先發(fā)送 MQ 消息,可能 MQ 消息發(fā)送成功,但是database由于某些原因更新失敗了。數(shù)據(jù)庫(kù)可以回滾,但是通常的MQ沒有回滾能力。
事務(wù)開始
1. database update
2. send to mq
事務(wù)結(jié)束
先更新數(shù)據(jù)庫(kù),再發(fā)送 MQ 消息,同樣會(huì)有問(wèn)題。就算1,2都執(zhí)行成功了,但是事務(wù)是需要提交的,數(shù)據(jù)庫(kù)有可能在提交階段失敗,數(shù)據(jù)庫(kù)是可以回滾,但是 MQ 的消息已經(jīng)發(fā)出去了,它并沒有回滾的能力。
為了解決這個(gè)問(wèn)題,我們同樣需要補(bǔ)償機(jī)制。在可靠消息服務(wù)一側(cè)開啟定時(shí)任務(wù),定時(shí)去查詢那些長(zhǎng)期處于“待發(fā)送”的事務(wù),再次對(duì) MQ 進(jìn)行投遞消息。這個(gè)機(jī)制有可能造成被動(dòng)方重復(fù)收到 MQ 的消息,這就需要被動(dòng)方處理業(yè)務(wù)的時(shí)候要進(jìn)行冪等處理。
總結(jié)
通過(guò)以上我們?cè)敿?xì)介紹了可靠消息最終一致性事務(wù)解決方案的總體結(jié)構(gòu)跟執(zhí)行的流程,以及對(duì)異常情況的一些補(bǔ)償方法,總體流程上還是比較清晰簡(jiǎn)單的。但是可靠消息最終一致性方案在使用上也是具有比較強(qiáng)的局限性,因?yàn)樗漠惒教匦愿锌赡艹霈F(xiàn)的高延時(shí)性不適合處理一些敏感業(yè)務(wù)。比如它適合處理消費(fèi)新增積分場(chǎng)景,但是不合適處理積分兌換禮品的場(chǎng)景。因?yàn)槿绻e分扣減延遲了,那么用戶就可能兌換超出本身積分多的多的禮品。所以我們選擇分布式事務(wù)的時(shí)候還需根據(jù)場(chǎng)景來(lái)進(jìn)行選擇。
好了講了這么多分布式事務(wù)的原理,下一期我們使用 .NET 真正的實(shí)現(xiàn)一個(gè)分布式事務(wù),敬請(qǐng)期待。
.Net Core with 微服務(wù) - 什么是微服務(wù)
.Net Core with 微服務(wù) - 架構(gòu)圖
.Net Core with 微服務(wù) - Ocelot 網(wǎng)關(guān)
.Net Core with 微服務(wù) - Consul 注冊(cè)中心
.Net Core with 微服務(wù) - Seq 日志聚合
.Net Core with 微服務(wù) - Elastic APM
.Net Core with 微服務(wù) - Consul 配置中心
.Net Core with 微服務(wù) - Polly 熔斷降級(jí)
.Net Core with 微服務(wù) - 分布式事務(wù) - 2PC、3PC
.Net Core with 微服務(wù) - 分布式事務(wù) - TCC
關(guān)注我的公眾號(hào)一起玩轉(zhuǎn)技術(shù)
本文摘自 :https://www.cnblogs.com/