RocketMQ是一款 分布式、隊列模型的消息中間件,由阿里巴巴團隊研發,借鑒參考了JMS規范的MQ實現,更參考了優秀的開源消息中間件KAFKA,并結合阿里實際業務需求,在天貓雙十一的場景,實現業務消峰、分布式事物的優秀框架。
其具有以下特點:
1.能夠保證嚴格的消息順序
2.提供豐富的消息拉取模式
3.高效的訂閱者水平擴展能力
4.實時的消息訂閱機制
5.億級消息堆積能力
RocketMQ的順序消息需要滿足2點:
1.Producer端保證發送消息有序,且發送到同一個隊列。
2.consumer端保證消費同一個隊列。
如何在集群消費時保證消費的有序呢?
1.ConsumeMessageOrderlyService類的start()方法,如果是集群消費,則啟動定時任務,定時向broker發送批量鎖住當前正在消費的隊列集合的消息,具體是consumer端拿到正在消費的隊列集合,發送鎖住隊列的消息至broker,broker端返回鎖住成功的隊列集合。consumer收到后,設置是否鎖住標志位。
這里注意2個變量:
consumer端的RebalanceImpl里的ConcurrentHashMap
processQueueTable,是否鎖住設置在ProcessQueue里。
broker端的RebalanceLockManager里的ConcurrentHashMap mqLockTable,這里維護著全局隊列鎖。
2.ConsumeMessageOrderlyService.ConsumeRequest的run方法是消費消息,這里還有個MessageQueueLock messageQueueLock,維護當前consumer端的本地隊列鎖。保證當前只有一個線程能夠進行消費。
3.拉到消息存入ProcessQueue,然后判斷,本地是否獲得鎖,全局隊列是否被鎖住,然后從ProcessQueue里取出消息,用MessageListenerOrderly進行消費。拉到消息后調用ProcessQueue.putMessage(final List msgs)存入,具體是存入TreeMap msgTreeMap。然后是調用ProcessQueue.takeMessags(final
int batchSize)消費,具體是把msgTreeMap里消費過的消息,轉移到TreeMap msgTreeMapTemp。
4.本地消費的事務控制,ConsumeOrderlyStatus.SUCCESS(提交),ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT(掛起一會再消費),在此之前還有一個變量ConsumeOrderlyContext context的setAutoCommit()是否自動提交。
當SUSPEND_CURRENT_QUEUE_A_MOMENT時,autoCommit設置為true或者false沒有區別,本質跟消費相反,把消息從msgTreeMapTemp轉移回msgTreeMap,等待下次消費。
當SUCCESS時,autoCommit設置為true時比設置為false多做了2個動作,consumeRequest.getProcessQueue().commit()和this.defaultMQPushConsumerImpl.getOffsetStore().updateOffset(consumeRequest.getMessageQueue(),commitOffset, false);
ProcessQueue.commit():本質是刪除msgTreeMapTemp里的消息,msgTreeMapTemp里的消息在上面消費時從msgTreeMap轉移過來的。
this.defaultMQPushConsumerImpl.getOffsetStore().updateOffset():本質是把拉消息的偏移量更新到本地內存中,然后定時更新到broker。
那么少了這2個動作會怎么樣呢,隨著消息的消費進行,msgTreeMapTemp里的消息堆積越來越多,消費消息的偏移量一直沒有更新到broker導致consumer每次重新啟動后都要從頭開始重復消費。就算更新了offset到broker,那么msgTreeMapTemp里的消息堆積呢?不知道這算不算bug所以,還是把autoCommit設置為true吧。
最后要感謝這個優秀的平臺,可以讓我們相互交流,如果想進一步學習交流,可以加群460570824,希望大家可以一起學習進步!