1. 程式人生 > >RocketMQ實戰(三):分散式事務

RocketMQ實戰(三):分散式事務

關於多Master多Slave的說明

由於在之前的部落格中已經搭建了雙Master,其實多Master多Slave大同小異,因此這裡並不會一步步的演示搭建多Master多Slave,而是從思路上,分析下重點應該注意的配置項。


多Master多Slave

第一,這四臺機器,對外是一個統一的整體,是一個rocketmq cluster,因此需要brokerClusterName保持統一

第二,123機器是121的從,124機器是122的從,如何在配置中體現? 主和從的brokerName需要保持一致,另外brokerId標示了誰是主,誰是從(brokerId=0的就是主,大於0的就是從)

第三,注意namesrvAddr的地址是4臺NameServer

第四,配置項中brokerRole需要指明 ASYNC_MASTER(非同步複製Master) or SYNC_MASTER(同步雙寫Master) or SLAVE(從)

第五,和以前的多Master啟動方式一致,先啟動4臺Namesrv,然後用指定配置檔案的方式啟動Master/Slave即可

第六,多Master多Slave的好處在於,即便叢集中某個broker掛了,也可以繼續消費,保證了實時性的高可用,但是並不是說某個master掛了,slave就可以升級master,開源版本的rocketmq是不可以的。也就是說,在這種情況下,slave只能提供讀的功能,將失去訊息負載的能力。

Queue in Topic

對於RocketMQ而言,Topic只是一個邏輯上的概念,真正的訊息儲存其實是在Topic中的Queue中。想一想,為什麼RocketMQ要這要設計呢?其實是為了訊息的順序消費,後文中將為大家介紹。


queue in topic
預設一個Topic中4個佇列
配置檔案中指定

初步認識RocketMQ的核心模組


rocketmq模組

rocketmq-broker:接受生產者發來的訊息並存儲(通過呼叫rocketmq-store),消費者從這裡取得訊息。

rocketmq-client:提供傳送、接受訊息的客戶端API。

rocketmq-namesrv:NameServer,類似於Zookeeper,這裡儲存著訊息的TopicName,佇列等執行時的元資訊。(有點NameNode的味道)

rocketmq-common:通用的一些類,方法,資料結構

rocketmq-remoting:基於Netty4的client/server + fastjson序列化 + 自定義二進位制協議

rocketmq-store:訊息、索引儲存等

rocketmq-filtersrv:訊息過濾器Server,需要注意的是,要實現這種過濾,需要上傳程式碼到MQ!【一般而言,我們利用Tag足以滿足大部分的過濾需求,如果更靈活更復雜的過濾需求,可以考慮filtersrv元件】

rocketmq-tools:命令列工具

Order Message

RocketMQ提供了3種模式的Producer:

NormalProducer(普通)、OrderProducer(順序)、TransactionProducer(事務)

在前面的部落格當中,涉及的都是NormalProducer,呼叫傳統的send方法,訊息是無序的。接下來,我們來看看順序消費。模擬這樣一個場景,如果一個使用者完成一個訂單需要3條訊息,比如訂單的建立、訂單的支付、訂單的發貨,很顯然,同一個使用者的訂單訊息必須要順序消費,但是不同使用者之間的訂單可以並行消費。

生產者端程式碼示例:


順序訊息模式

注意,一個Message除了Topic/Tag外,還有Key的概念。

上圖的send方法不同於以往,有一個MessageQueueSelector,將用於指定特定的訊息發往特定的隊列當中!


順序消費

注意在以前普通消費訊息時設定的回撥是MessageListenerConcurrently,而順序消費的回撥設定是MessageListenerOrderly。

當我們啟動2個Consumer進行消費時,可以觀察到:


多個消費者消費的結果

可以觀察得到,雖然從全域性上來看,訊息的消費不是有序的,但是每一個訂單下的3條訊息是順序消費的!

其實,如果需要保證訊息的順序消費,那麼很簡單,首先需要做到一組需要有序消費的訊息發往同一個broker的同一個佇列上!其次消費者端採用有序Listener即可。

這裡,RocketMQ底層是如何做到訊息順序消費的,看一看原始碼你就能大概瞭解到,至少來說,在多執行緒消費場景下,一個執行緒只去消費一個佇列上的訊息,那麼自然就保證了訊息消費的順序性,同時也保證了多個執行緒之間的併發性。也就是說其實broker並不能完全保證訊息的順序消費,它僅僅能保證的訊息的順序傳送而已!

關於多執行緒消費這塊,RocketMQ早就替我們想好了,這樣設定即可:


消費多執行緒設定

想一想,在ActiveMQ中,我們如果想實現併發消費的話,恐怕還得搞個執行緒池提交任務吧,RocketMQ讓我們的工作變得簡單!

Transaction Message

在說事務訊息之前,我們先來說說分散式事務的那些事!

什麼是分散式事務,我的理解是一半事務。怎麼說,比如有2個異構系統,A異構系統要做T1,B異構系統要做T2,要麼都成功,要麼都失敗。

要知道異構系統,很顯然,不在一個資料庫例項上,它們往往分佈在不同物理節點上,本地事務已經失效。


2階段提交

2階段提交協議,Two-Phase Commit,是處理分散式事務的一種常見手段。2PC,存在2個重要角色:事務協調器(TC),事務執行者。

2PC,可以看到節點之間的通訊次數太多了,時間很長!時間變長了,從而導致,事務鎖定的資源時間也變長了,造成資源等待時間變長!在高併發場景下,存在嚴重的效能問題!

下面,我們來看看MQ在高併發場景下,是如何解決分散式事務的。

考慮生活中的場景:

我們去北京慶豐包子鋪吃炒肝,先去營業員那裡付款(Action1),拿到小票(Ticket),然後去取餐視窗排隊拿炒肝(Action2)。思考2個問題:第一,為什麼不在付款的同時,給顧客炒肝?如果這樣的話,會增加處理時間,使得後面的顧客等待時間變長,相當於降低了接待顧客的能力(降低了系統的QPS)。第二,付了款,拿到的是Ticket,顧客為什麼會接受?從心理上說,顧客相信Ticket會兌現炒肝。事實上也是如此,就算在最後炒肝沒了,或者斷電斷水(系統出現異常),顧客依然可以通過Ticket進行退款操作,這樣都不會有什麼損失!(雖然這麼說,但是實際上包子鋪最大化了它的利益,如果炒肝真的沒了,浪費了顧客的時間,不過顧客頂多發發牢騷,最後接受)

生活已經告訴我們處理分散式事務,保證資料最終一致性的思路!這個Ticket(憑證)其實就是訊息!


業務和訊息生成耦合在一起

業務操作和訊息的生成耦合在一起,保證了只要A銀行的賬戶發生扣款,那麼一定會生成一條轉賬訊息。只要A銀行系統的事務成功提交,我們可以通過實時訊息服務,將轉賬訊息通知B銀行系統,如果B銀行系統回覆成功,那麼A銀行系統可以在table中設定這條轉賬訊息的狀態。

這樣耦合的方式,從架構上來看,就有點不太優雅,而且存在一些問題。比如說,訊息的儲存實質上是在A銀行系統中的,如果A銀行系統出了問題,將導致無法轉賬。如果解耦,將訊息獨立出來呢?


業務和訊息解耦

如上圖所示,訊息資料獨立儲存,業務和訊息解耦,實質上訊息的傳送有2次,一條是轉賬訊息,另一條是確認訊息。

到這裡,我們先來看看基於RocketMQ的程式碼:


生產者示例程式碼

生產者這裡用到是:TransactionMQProducer。

這裡涉及到2個角色:本地事務執行器(程式碼中的TransactionExecuterImpl)、伺服器回查客戶端Listener(程式碼中的TransactionCheckListener)。

如果事務訊息傳送到MQ上後,會回撥  本地事務執行器;但是此時事務訊息是prepare狀態,對消費者還不可見,需要  本地事務執行器  返回RMQ一個確認訊息。


本地事務執行器

事務訊息是否對消費者可見,完全由事務返回給RMQ的狀態碼決定(狀態碼的本質也是一條訊息)。


回查Listener
執行結果

生產者傳送了2條訊息給RMQ,有一條本地事務執行成功,有一條本地事務執行失敗。

2條業務訊息 + 2條確認訊息  因此是4條;

注意到消費者只消費了一條資料,就是隻有告訴RMQ本地事務執行成功的那條訊息才會被消費!因此是1條!

但是,注意到本地事務執行失敗的訊息,RMQ並沒有check listener?這是為什麼呢?因為RMQ在3.0.8的時候還是支援check listener回查機制的,但是到了3.2.6的時候將事務回查機制“閹割”了!

那麼3.0.8的時候,RMQ是怎麼做事務回查的呢?看一看原始碼,你會知道,其實事務訊息開始是prepare狀態,然後RMQ會將其持久化到MySQL當中,然後如果收到確認訊息,就刪除掉這條prepare訊息,如果遲遲收不到確認訊息,那麼RMQ會定時的掃描prepare訊息,傳送給produce group進行回查確認!

到這裡,問題來了,要知道3.2.6版本,沒有回查機制了,會存在問題麼?

當然會存在問題!假設,我們傳送一條轉賬事務訊息給RMQ,成功後回撥本地事務,DB減操作成功,剛準備給RMQ一個確認訊息,此時突然斷電,或者網路抖動,使得這條確認訊息沒有傳送出去。此時RMQ中的那條轉賬事務訊息,始終處於prepare狀態,消費者讀取不到,但是卻已經完成一方的賬戶資金變動!!!

既然,RMQ3.2.6版本不為我們進行回查,那麼只能由我們自己完成了。具體怎麼做呢,咱們下期再來分析~