初步接觸了RocketMQ后發(fā)現其與傳統意義上的實現JMS協議的消息隊列(如ActiveMQ)存在著不小的區(qū)別,很有必要對其中的一些概念做個說明。
NameServer集群
NameServer的作用是注冊中心,類似于Zookeeper,但又有區(qū)別于它的地方。每個NameServer節(jié)點互相之間是獨立的,沒有任何信息交互,也就不存在任何的選主或者主從切換之類的問題,因此NameServer與Zookeeper相比更輕量級。單個NameServer節(jié)點中存儲了活躍的Broker列表(包括master和slave),這里活躍的定義是與NameServer保持有心跳。
Broker集群
Broker是具體提供業(yè)務的服務器,單個Broker節(jié)點與所有的NameServer節(jié)點保持長連接及心跳,并會定時將Topic信息注冊到NameServer,順帶一提底層的通信和連接都是基于Netty實現的。
Broker中分master和slave兩種角色,每個master可以對應多個slave,但一個slave只能對應一個master,master和slave通過指定相同的Brokername,不同的BrokerId (master為0)成為一個組。master和slave之間的同步方式分為同步雙寫和異步復制,異步復制方式master和slave之間雖然會存在少量的延遲,但性能較同步雙寫方式要高出10%左右。
另外,Broker中還存在一些非常重要的名詞需要說明:
Topic和Queue
RocketMQ的Topic/Queue和JMS中的Topic/Queue概念有一定的差異,JMS中所有消費者都會消費一個Topic消息的副本,而Queue中消息只會被一個消費者消費;但到了RocketMQ中Topic只代表普通的消息隊列,而Queue是組成Topic的更小單元,集群消費模式下一個消費者只消費該Topic中部分Queue中的消息,當一個消費者開啟廣播模式時則會消費該Topic下所有Queue中的消息
Tags
Tags是Topic下的次級消息類型(注:Tags也支持TagA || TagB這樣的表達式),可以在同一個Topic下基于Tags進行消息過濾。Tags的過濾需要經過兩次比對,首先會在Broker端通過Tag hashcode進行一次比對過濾,匹配成功傳到consumer端后再對具體Tags進行比對,以防止Tag hashcode重復的情況。Queue中具體的存儲單元結構如下圖:

Queue單個存儲單元結構.png
Producer集群
與nameserver的關系
單個Producer和一臺nameserver保持長連接,定時查詢topic配置信息,如果該nameserver掛掉,生產者會自動連接下一個nameserver,直到有可用連接為止,并能自動重連。與nameserver之間沒有心跳。
與broker的關系
單個Producer和與其關聯的所有broker保持長連接,并維持心跳。默認情況下消息發(fā)送采用輪詢方式,會均勻發(fā)到對應Topic的所有queue中。
最佳實踐
一個應用盡可能只使用一個 Topic,消息子類型用 tags 來標識,tags 可以由應用自由設置。只有發(fā)送消息設置了tags,消費方在訂閱消息時,才可以利用 tags 在 broker 做消息過濾。
每個消息在業(yè)務層面的唯一標識碼,要設置到 keys 字段,方便將來定位消息丟失問題。服務器會為每個消
息創(chuàng)建索引(哈希索引),應用可以通過 Topic,key 來查詢返條消息內容,以及消息被誰消費。由于是哈希索引,請務必保證 key 盡可能唯一,這樣可以避免潛在的哈希沖突。
消息發(fā)送成功或者失敗,要打印消息日志,務必要打印 sendresult 和 key 字段。
對于消息不可丟失應用,務必要有消息重發(fā)機制。例如:消息發(fā)送失敗,存儲到數據庫,能有定時程序嘗試重發(fā)或者人工觸發(fā)重發(fā)。
某些應用如果不關注消息是否發(fā)送成功,請直接使用sendOneWay方法發(fā)送消息。
Consumer集群
與nameserver的關系
單個Consumer和一臺nameserver保持長連接,定時查詢topic配置信息,如果該nameserver掛掉,消費者會自動連接下一個nameserver,直到有可用連接為止,并能自動重連。與nameserver之間沒有心跳。
與broker的關系
單個Consumer和與其關聯的所有broker保持長連接,并維持心跳,失去心跳后,則關閉連接,并向該消費者分組的所有消費者發(fā)出通知,分組內消費者重新分配隊列繼續(xù)消費。
最佳實踐
Consumer 數量要小于等于queue的總數量,由于Topic下的queue會被相對均勻的分配給Consumer,如果 Consumer 超過queue的數量,那多余的 Consumer 將沒有queue可以消費消息。
消費過程要做到冪等(即消費端去重),RocketMQ為了保證性能并不支持嚴格的消息去重。
盡量使用批量方式消費,RocketMQ消費端采用pull方式拉取消息,通過consumeMessageBatchMaxSize參數可以增加單次拉取的消息數量,可以很大程度上提高消費吞吐量。另外,提高消費并行度也可以通過增加Consumer處理線程的方式,對應參數consumeThreadMin和consumeThreadMax。
消息發(fā)送成功或者失敗,要打印消息日志。
補充
線上建議關閉autoCreateTopicEnable配置
該配置用于在Topic不存在時自動創(chuàng)建,會造成的問題是自動新建的Topic只會存在于一臺broker上,后續(xù)所有對該Topic的請求都會局限在單臺broker上,造成單點壓力。
broker master宕機情況是否會丟消息
broker master宕機,雖然理論上來說不能向該broker寫入但slave仍然能支持消費,但受限于rocketmq的網絡連接機制,默認情況下最多需要30秒,消費者才會發(fā)現該情況,這個時間可通過修改參數pollNameServerInteval來縮短。這個時間段內,發(fā)往該broker的請求都是失敗的,而且該broker的消息無法消費,因為此時消費者不知道該broker已經掛掉。 直到消費者得到master宕機通知后,才會轉向slave進行消費,但是slave不能保證master的消息100%都同步過來了,因此會有少量的消息丟失。但是消息最終不會丟,一旦master恢復,未同步過去的消息仍然會被消費掉。