rocketmq事務消息源碼解析

開篇

rocketmq 事務消息是常用的一種實現分佈式事務弱一致性的技術方案,以rocketmq broker爲事務管理器的角色,以兩階段的方式來做事務管理,本文默認你已經熟悉rocketmq的使用,我們一起做更深入的源碼探討。

我們爲什麼要用它

大部分技術或者說從我們現在可以接觸到的來說,還都是在別人已經走的路上,既然是路那麼一定有一個方向和目的,我們的目的就是爲了解決分佈式環境中的分佈式事務問題,隨着分佈式的普及,系統支持橫行擴容基本成爲系統的基本屬性,高的擴展性是我們所必須的,但是他也會帶來一些問題,就是以前常用的一些的基本操作,事務和線程同步,都不在有效,我們必須也要尋找一種更高一層緯度的實現,當然這其中也設計到一個cap理論,我們的事務消息也就是一種分佈式事務的實現方案

交互流程

這裏只做大概的說明,他的主要思路就是大事務 = 小事務 + 異步,中間在加一些保證機制,保證這些小事務都可以成功,如果不可以成功就一起失敗,主要交互如下
在這裏插入圖片描述
可以看出他主要是通過,mq自己回查判斷,上游事務是否執行完成,來判斷要不要發送消息觸發下游事務,而下游事務主要通過rocketmq的對於事務可達性的保證來,不斷重複調用,在一定配置內,直到下游事務返回事務執行成功。可以看出主要工作都是rocketmq來做的,由他來確保上游是否執行成功,在由他來確保下游一定執行成功,可以看出這種方式,不會對上游進行回滾,因此適合有主從概念的場景,比如支付和發短信。

說之前要了解的基礎

在瞭解這三大步驟之前我們要知道一些基礎的類

message

我們消息傳遞的消息體,這個是最基本的,在rocketmq中,主要有一下分類
在這裏插入圖片描述
他們的類關係圖如下
在這裏插入圖片描述
可以看出message就是最基本的一個消息的定義,真正我們在broker中使用的是messageExt,它裏面包含了幾乎全部我們broker要處理和判斷的屬性,而MessageExtBrokerInner 只是messageExt的序列化實現,主要包含一個字符串,存儲我們的全部屬性,方便我們消息存入文件。

messageExt

我們重點說明一下messageExt這個類中屬性,如下
在這裏插入圖片描述
最後一個屬性補充如下,preparedTransactionOffset ,就是事務消息中的位點大小,可以看出一條消息獨自存儲了,所有自己的相關屬性

三個隊列

我們要知道message在broker中存儲的方式,和mq(消息隊列)對於消息隊列的定義方式,只有瞭解這些我們才方便了解他是怎麼處理事務消息的,三個隊列如下
三個隊列

commitLog rocketmq存儲消息的文件

這就是一個二進制的序列化後的文件,保存了全部的消息,通常是1G一個文件,這個大小可以自己自由配置,下面簡單的放一下結構,瞭解就可以
在這裏插入圖片描述
其實我們也可以看到和我們messageExt對象可以對應的起來,他的每一個字節大小都是有自己獨特的規定的,不能亂,一旦亂了,會導致整個commitLog文件損壞不可以讀,物理存儲如下
在這裏插入圖片描述

consumeQueue 存儲的是commitLog的索引,代表的是一個單獨的隊列

我們的topic很多,但是現在消息都實際存儲在commitLog中,不可能所有消息當要讀取的時候,都要掃描我們的commitLog,來找到自己的topic存儲的消息,所以我們多來一個consumeQueue,單獨存取我們同一個topic下的消息,我們只需要掃描consumeQueue來就可以找到我們的同一個topic下的消息,但是一個消息隊列,也就是一個topic也有大量的讀取和寫入,如果只有一個文件的話,也會有併發問題,所以我們在對consumeQueue 進行拆分,犧牲全局順序性,來滿足消息隊列最重要的高性能,這樣我們就支持進一步支持來大的併發。物理存儲如下
在這裏插入圖片描述
下面在展示一下他的存儲結構
在這裏插入圖片描述
以上就是rocketMq的兩個物理存儲文件

messageQueue 消息的邏輯隊列

messageQueue是在內存中的,前面我們瞭解了,我們已經在物理方面對mq的文件存儲性能進行來擴展,讓他可以最大限度的支持多線程的讀寫,那麼我們怎麼設計我們多個消費者組,或者同一個消費者組內部多個消費者對於同一個topic消費呢,答案就是引入邏輯隊列messageQueue。
** 注意 在集羣模式下,一個messageQueue 只對應一個consumer**
通過上面的說明我們知道,一個topic在一個broker上去會有多個 物理上consumeQueue,consumeQueue就是一排消息索引,沒有什麼狀態,我們要消費一條消息,或者說找到一條消息,就要知道這個消息是在哪一個consumeQueue上哪一個offset,從這裏我們看出來 consumeQueue 設計出來是爲了保存消息的,那他怎麼處理當我有多個不同的consumeGroup的consumer來消費同一個consumeQueue,保證每一個consumer都可以消費自己的消息,那怕consumeQueue 內部consumer切換,也可以做到不亂消費呢,答案就是用messageQueue。messageQueue會爲每一個來消費的consumeGroup保存一個 offset,記錄他的consumeGroup在這個consumeQueue上消費到一個位置,這樣就可以讓每個人都以自己的offset來消費,但是隊列都是相同的。
下面我們來看看messageQueue的結構,和他怎麼管理不同consumer的offset
在這裏插入圖片描述

兩個特殊的topic

在事務消息中會涉及到兩個特殊的topic,我們會簡單的說說他

RMQ_SYS_TRANS_OP_HALF_TOPIC(op隊列)

這個是事務消息本地事務完成確認隊列,所有收到的事務消息本地事務最終狀態都會新建一個body爲messageExt.getQueueOffset(),topic爲RMQ_SYS_TRANS_OP_HALF_TOPIC的消息存下來

RMQ_SYS_TRANS_HALF_TOPIC(half隊列)

這個是事務消息待確認消息隊列,主要是所有收到的事務消息預提交,就是還沒有確認本地事務執行結果的消息都會把他的topic改爲他,在保存起來

源碼分析

下面我們開始源碼分析,從上面可以看出rocketmq 主要有三大交互

  1. producer 發送事務消息給 broker
  2. broker 發送消息給 producer,詢問本地事務是否執行成功
  3. producer 告知broker本地事務執行結果,這裏包含兩個觸發
    1. 本地事務支持完成,自動發送給broker
    2. 收到broker的詢問,執行ckeck方法,告知本地事務執行結果

第一個流程: producer 發送事務消息給 broker

這個流程主要是,broker接受消息時會判斷是不是事務消息,如果是的話,就複製一條消息,修改這條消息的topic爲RMQ_SYS_TRANS_OP_HALF_TOPIC,在把原來的topic放入我們的message屬性集中主要流程如下
在這裏插入圖片描述
上面就是我一步一步對着源碼花的流程圖,創作不易,請給我點贊和評論呀

第二個流程: broker 發送消息給 producer,詢問本地事務是否執行成功

這個主要是內部job觸發一分鐘一次,掃描和比對兩個隊列(op和half隊列),來判斷那些消息還沒有收到客戶端的本地事務消息確認回答,找到了就把這一批匯總髮送給客戶端,進行確認

定時任務

broker內部有一個定時任務框架 serviceThread,主要是用thread和CountDownLatch2來實現的,事務消息check是1分鐘執行一次,代碼如下
在這裏插入圖片描述
可以看出就是執行前打一個標誌,然後不停的等,等待超時後在重新執行

主要流程

在這裏插入圖片描述
上面就是我畫的流程圖,創作不易,各位點贊呀,可以對比這源碼一步一步的來看,這個是最複雜的一個流程,它複雜在主要是對一個消息是否觸發回調check的判斷,包含了很多緯度

  1. 消息回調的次數
  2. 消息已經存活的時間
  3. 本地事務消息設置的超時時間
  4. 全局事務消息設置的超時時間
  5. 服務器雙方的時間
    等還有一些很細節的都標註在圖了,這一塊一定到細心

第三個流程: producer 告知broker本地事務執行結果,這裏包含兩個觸發

這個結合這前面兩個看就不要簡單了,他就是當收到producer發送過來的本地事務消息確認時,把消息放到op隊列中就沒了,流程如下
在這裏插入圖片描述

總結

基本上上面就是一次事務消息broker的整個處理流程,如果自己在生產中有問題,可以對着圖來進行比對,也可以聯繫我,加我微信,我們一起分析,克服問題共同成長,可以的話,求打賞

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章