QMQ和RocketMQ簡單分享

最近在組內(nèi)做了中間件的簡單分享,主題是流行的開源中間件RocketMQ和我司開源的QMQ之間的實現(xiàn)和區(qū)別。

RocketMQ最早由阿里開源,是相當(dāng)流行的一款消息中間件,QMQ最早在我司內(nèi)部使用,后來也開源了。兩者的實現(xiàn)原理大致相同,但是細(xì)節(jié)之處還是有很多不一樣的地方,這些不同之處的實現(xiàn)也很值得思考。

架構(gòu)介紹

1.png

RocketMQ的架構(gòu)使用過的人應(yīng)該都很熟悉,分為Producer,Consumer,Broker,NameServer四大集群,NameServer本身無狀態(tài),主要是維護(hù)Broker的地址等路由信息,Broker是消息的中轉(zhuǎn)站,還負(fù)責(zé)消息的存儲等。


2.png

QMQ的架構(gòu)與RocketMQ大同小異,meta server負(fù)責(zé)狀態(tài)和路由信息的維護(hù),server的功能與broker相當(dāng),但是延時和定時消息被單獨拆分出了一個服務(wù)delay server。我想這么做也是由業(yè)務(wù)決定的,酒店,度假,機(jī)票訂單都經(jīng)常會有提前預(yù)定的情況,消息并不需要實時消費,類似的業(yè)務(wù)場景比較常見。



消息存儲與消費

RocketMQ存儲
3.png

RocketMQ對消息的存儲是混合型的結(jié)構(gòu),單一broker下所有的消息都保存在commitlog一個文件中。邏輯上是如此,實際上還是分了很多個文件的,單個文件最大的大小是1G,文件名是起始偏移量。單一commitlog是一個相對抽象的概念。除了commitlog之外 ,還有consumequeue和indexfile兩個索引文件。consumequeue顧名思義,是用來給消費者讀取的,broker會把要消費的消息推到consumequeue中,每一個consumequeue都對應(yīng)了一個消費者,但也不是一一對應(yīng)的關(guān)系,多個consumequeue是可以對應(yīng)到同一個消費者的。broker接收到來自生產(chǎn)者的消息后,寫入commitlog并且刷盤,隨后將消息的offset等信息負(fù)載一個消費隊列,然后寫入。



QMQ消息存儲

在了解QMQ的設(shè)計之前,我想先說說RocketMQ的存儲設(shè)計有哪些缺點。對于ConsumeQueue,或者叫partition,數(shù)量固定的情況下,如果消費者客戶端與其數(shù)量不一致,那很容易發(fā)生一個消費者客戶端負(fù)載了多個消息隊列從而導(dǎo)致很忙碌,但是其他客戶端很空閑的情況,即負(fù)載不均衡,同時擴(kuò)容也不太方便,增加機(jī)器并不能實時的增加ConsumeQueue與之對應(yīng)?;谶@個問題,QMQ對存儲模型做了改進(jìn)。


4.png

存儲還是分了三個部分,分別是message log, consume log, pull log。messagelog與RocketMQ的commitlog作用基本一致,consume log作用和consumeQueue作用也基本一致,pull log是QMQ增加在兩者之間的中間層,用來方便擴(kuò)容

  • message log 存儲生產(chǎn)者發(fā)送來的消息,是消息的存儲單元
  • consume log一個log對應(yīng)一個topic,存儲的是commit log的索引信息
  • pull log存儲的是consume log的索引信息,每一個Pull log對應(yīng)了一個消費者客戶端。


    這樣一來,消費隊列與消費者之間的關(guān)系被解耦,當(dāng)消費者增加時,新增一個Pull log,就可以實時負(fù)載到topic下對應(yīng)的消息,積壓的消息也可以被及時消費。
    網(wǎng)上關(guān)于QMQ能找到的資料不多,對于這樣的存儲模型,我也還存在幾個問題,希望日后能找到答案。
  1. 消息的積壓是積壓在consume log中還是Pull log中?按照能實時擴(kuò)容的功能分析,應(yīng)該是積壓在consume log中,那么消費者客戶端在拉取的時候是實時的將consume log的偏移量推到pull log中嗎?這樣是否會影響性能,或者造成重復(fù)消費
  2. 增加一層中間層對性能的影響如何



事務(wù)消息

RocketMQ事務(wù)消息

RocketMQ對事務(wù)消息的理念是,保證最終一致性,通過兩階段提交的方式來實現(xiàn)生產(chǎn)者端與消費者端的狀態(tài)一致。


5.png

如圖所示,生產(chǎn)者推送一個prepare到broker,broker將消息記錄,并且定時向生產(chǎn)者輪詢。
生產(chǎn)者可以配置在消息消費成功之后的邏輯。

QMQ事務(wù)消息
6.png

如圖所示,QMQ的事務(wù)是通過關(guān)系型數(shù)據(jù)庫來實現(xiàn)。即每一個使用QMQ的應(yīng)用,都必須新建一個指定的table,用來保存要發(fā)送的消息。在server上會有定時的任務(wù)來輪詢db,將未消費的消息進(jìn)行消費。同時,生產(chǎn)者也可以將業(yè)務(wù)邏輯與消息寫入db放到同一個數(shù)據(jù)庫事務(wù)中,保證了業(yè)務(wù)邏輯和消息發(fā)送成功這兩個操作的原子性。
但是作為一個消息中間件,部署時必須要依賴db,復(fù)雜度略有增加。
并且,消息發(fā)送的可靠性需要和業(yè)務(wù)的寫db綁定,也就是放在同一個事務(wù)中,對業(yè)務(wù)邏輯有一定的侵入性。
同時還會帶來一個問題,當(dāng)一個方法只是做純消息轉(zhuǎn)發(fā),沒有任何其他的業(yè)務(wù)邏輯去操作db,那么如何保證消息持久化失敗之后的補(bǔ)償處理呢?對于這個場景,QMQ提供了MessageSendStateListener來監(jiān)聽發(fā)送后的狀態(tài),該接口存在兩個方法,onSuccess和onFailed,可以對發(fā)送失敗后的邏輯自定義。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容