說明
週五的時候發了篇:Rocketmq4.3支援事務啦!!!,趁著週末的時候把相關內容看了下,下面的主要內容就是關於RocketMQ事務相關內容介紹了。
說明: 今天這篇僅僅是入門介紹,並沒有涉及到很多細節,先把大概流程說明白,後續再具體細節進行開篇說明。
主題
- 引出分散式事務相關內容。
- RocketMQ事務訊息。
- RocketMQ事務訊息如何使用。
- RocketMQ事務訊息是怎麼實現的。
- 為什麼需要事務訊息會查機制。
- RocketMQ是怎麼進行事務訊息會查的。
- RocketMQ對於分散式事務解決還有那些侷限性?以及說明。
引出分散式事務相關內容
這裡主要是想說明下,是什麼背景下面產生了此類問題。
首先我們來說說事務,說道事務,首先讓我想到的就是我大學的時候,老師經常舉例轉賬的事情,例子是這樣的:
銀行轉賬!張三轉100塊到李四的賬戶,這其實需要兩條SQL語句:
- 給張三的賬戶減去100元
- 給李四的賬戶加上100元
如果在第一條SQL語句執行成功後,在執行第二條SQL語句之前,程式被中斷了(可能是丟擲了某個異常,也可能是其他什麼原因),那麼李四的賬戶沒有加上100元,而張三卻減去了100元。這肯定是不行的!
你現在可能已經知道什麼是事務了吧!事務中的多個操作,要麼完全成功,要麼完全失敗!不可能存在成功一半的情況!也就是說給張三的賬戶減去100元如果成功了,那麼給李四的賬戶加上100元的操作也必須是成功的;否則給張三減去100元,以及給李四加上100元都是失敗的!
如果在單個服務裡面,我們使用spring的話,我們使用一個註解就解決了這個問題(@Transactional註解就解決了,基本上就沒有下文什麼事情了。)
隨著各各公司服務越來越複雜,資料量越來越多,慢慢的公司就引入了微服務以及分庫分表等技術,這些技術的確非常的有用,但是在解決事務方面帶來了一下問題。
強調下:分散式事務,我們一般都是強調的最終一致性,而不是強一致性!!!
主要概括為3類:
- 基於單個JVM,資料庫分庫分表了(跨多個資料庫)。
- 基於多JVM,服務拆分了(不跨資料庫)。
- 基於多JVM,服務拆分了 並且資料庫分庫分表了。
正是為了解決上面的3類問題,才引入了分散式事務相關的技術來解決這些問題。
分散式事務解決方案有很多,本篇下面需要說的RocketMQ事務訊息僅僅是其中的一種解決方案。
RocketMQ事務訊息
這裡主要是參考RocketMQ官方文件裡面的內容,參考地址:
http://rocketmq.apache.org/rocketmq/the-design-of-transactional-message/
Half(Prepare) Message
指的是暫不能投遞的訊息,傳送方已經將訊息成功傳送到了 MQ 服務端,但是服務端未收到生產者對該訊息的二次確認,此時該訊息被標記成“暫不能投遞”狀態,處於該種狀態下的訊息即半訊息。
訊息回查
由於網路閃斷、生產者應用重啟等原因,導致某條事務訊息的二次確認丟失,MQ 服務端通過掃描發現某條訊息長期處於“半訊息”時,需要主動向訊息生產者詢問該訊息的最終狀態(Commit 或是 Rollback),該過程即訊息回查。
執行流程圖
- 傳送方向 MQ 服務端傳送訊息。
- MQ Server 將訊息持久化成功之後,向傳送方 ACK 確認訊息已經傳送成功,此時訊息為半訊息。
- 傳送方開始執行本地事務邏輯。
- 傳送方根據本地事務執行結果向 MQ Server 提交二次確認(Commit 或是 Rollback),MQ Server 收到 Commit 狀態則將半訊息標記為可投遞,訂閱方最終將收到該訊息;MQ Server 收到 Rollback 狀態則刪除半訊息,訂閱方將不會接受該訊息。
- 在斷網或者是應用重啟的特殊情況下,上述步驟4提交的二次確認最終未到達 MQ Server,經過固定時間後 MQ Server 將對該訊息發起訊息回查。
- 傳送方收到訊息回查後,需要檢查對應訊息的本地事務執行的最終結果。
- 傳送方根據檢查得到的本地事務的最終狀態再次提交二次確認,MQ Server 仍按照步驟4對半訊息進行操作。
事務訊息傳送對應步驟1、2、3、4,事務訊息回查對應步驟5、6、7。
RocketMQ事務訊息如何使用
1、引入 rocketmq-client
目前還沒有,等幾天就會有了,或者自己下載原始碼進行打包到自己的私有倉庫也是一樣的,經過和4.2.0的引入差不多,就是換成4.3.0即可。
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.3.0</version>
</dependency>
複製程式碼
2、編寫Producer
主要參考RocketMQ4.3.0裡面的例子,地址為:
https://github.com/apache/rocketmq/tree/release-4.3.0/example/src/main/java/org/apache/rocketmq/example/transaction
備註:和普通訊息傳送DefaultMQProducer有所不同,這裡使用的是TransactionMQProducer。
關鍵點: 自己實現TransactionListener介面,並實現executeLocalTransaction方法(執行本地事務的,一般就是操作DB相關內容)和checkLocalTransaction方法(用來提供給broker進行會查本地事務訊息的,把本地事務執行的結果儲存到redis或者DB中都可以,為會查做資料準備)。
還是以:給張三的賬戶減去100元, 給李四的賬戶加上100元為例。這個時候我們傳送就相當於做的是張三的賬戶減去100元的操作,並且操作DB成功了,下面就是另外對李四的賬戶加上100元的操作了,這個時候就是相當於定義剛剛傳送事務訊息的topic,消費端和我們普通的消費端沒有什麼區別。
思考:假如這個時候我們消費端失敗了怎麼辦呢?(這個問題後續有討論,消費失敗有2種,第一種是超時了,我們重試即可,第二種是真的處理失敗了?該怎麼辦呢?)
RocketMQ事務訊息是怎麼實現的
第一感覺和定時訊息做法非常類似,但是比定時訊息要複雜。定時訊息內容在:RocketMQ(九):訊息傳送(續)裡面提到過。
本質: 定時訊息先把定時訊息的topic修改為SCHEDULE_TOPIC_XXXX,之後一系列處理……,我們這個事務訊息也是一樣,先傳送Half(Prepare) Message訊息的時候,其實topic內容也繼續了修改(RMQ_SYS_TRANS_HALF_TOPIC),所有consumer是不可見的(如果提交就用真實topic,如果需要回滾那麼就那臨時的topic內容刪除即可。)
Half(Prepare) Message修改topic斷點如下:
本篇僅僅是一個入門介紹,裡面具體細節後續章節會進行分析。
思考: 如果把傳送普通訊息和本地執行邏輯放在一個事務裡面,如果執行事務成功就傳送普通訊息,如果失敗就回滾好像也是可以,那麼這種事務訊息相對其有什麼優勢或者好處呢??? 思考下。
為什麼需要事務訊息會查機制
其實這個內容在,RocketMQ事務訊息介紹裡面也說明了,由於網路閃斷、生產者應用重啟等原因,導致某條事務訊息的二次確認丟失,MQ 服務端通過掃描發現某條訊息長期處於“半訊息”時,需要主動向訊息生產者詢問該訊息的最終狀態(Commit 或是 Rollback),該過程即訊息回查。那麼RocketMQ到底怎麼做的呢?下面馬上就會介紹。
RocketMQ是怎麼進行事務訊息會查的
每60s會對Half(Prepare) Message的topic主題為RMQ_SYS_TRANS_HALF_TOPIC的訊息進行check。
broker會呼叫自己實現TransactionListener介面的checkLocalTransaction方法。
備註: 就是RocketMQ已經實現這個機制,今天這篇僅僅是入門介紹,並沒有涉及到很多細節,先把大概流程說明白,後續再具體細節進行開篇說明。
RocketMQ對於分散式事務解決還有那些侷限性?以及說明
在RocketMQ事務訊息如何使用的時候我們提到,**如果消費失敗怎麼辦?**消費失敗有2種,第一種是超時了,我們重試即可,第二種是真的處理失敗了?仔細思考下,這塊還是蠻複雜的,假如需要有7-8個業務模組呢,其中執行到第6個業務模組就失敗呢? **這種重試好幾次還是失敗,我們該如何處理呢???**是回滾前面5個操作嗎?好複雜好複雜,為什麼RocketMQ不提供自動回滾呢? 希望大家思考下,如果失敗率比較大,那麼就是系統問題需要優化程式碼業務……
據零度瞭解,很多這塊是通過人工介入以及T+1對賬的以及補償機制等。
結束語
本人水平有限,難免會有一些理解偏差的地方,如果發現,歡迎各位積極指出,感謝!!!
如果讀完覺得有收穫的話,歡迎點贊、關注、加公眾號【匠心零度】,查閱更多精彩歷史!!!