1. 程式人生 > >RocketMQ 關鍵特性

RocketMQ 關鍵特性

自己的 線程 圖片 結構 page 數據丟失 apach 其他 ash

Apache RocketMQ之所以能在眾多的消息中間件中脫穎而出,能吸引數千企業用戶與RocketMQ的關鍵特性是分不開的,本文詳細介紹RocketMQ中的關鍵特性。

一、過萬的單機隊列數
諸如Kafka之類的消息中間件,在隊列數上升時性能會產生巨大的損失,RocketMQ之所以能單機支持上萬的持久化隊列與其獨特的存儲結構分不開。

技術分享圖片

如上圖所示,所有的消息數據單獨存儲到一個Commit Log,完全順序寫,隨機讀。對最終用戶展現的隊列實際只存儲消息在Commit Log的位置信息,並且串行方式刷盤。

這樣做的好處如下:

隊列輕量化,單個隊列數據量非常少。
對磁盤的訪問串行化,避免磁盤竟爭,不會因為隊列增加導致IOWAIT增高。
每個方案都有缺點,它的缺點如下:

寫雖然完全是順序寫,但是讀卻變成了完全的隨機讀。
讀一條消息,會先讀Consume Queue,再讀Commit Log,增加了開銷。
要保證Commit Log與Consume Queue完全的一致,增加了編程的復雜度。
以上缺點如何克服:

隨機讀,盡可能讓讀命中PAGECACHE,減少IO讀操作,所以內存越大越好。如果系統中堆積的消息過多,讀數據要訪問磁盤會不會由於隨機讀導致系統性能急劇下降,答案是否定的。

訪問PAGECACHE時,即使只訪問1k的消息,系統也會提前預讀出更多數據,在下次讀時,就可能命中內存。
隨機訪問Commit Log磁盤數據,系統IO調度算法設置為NOOP方式,會在一定程度上將完全的隨機讀變成順序跳躍方式,而順序跳躍方式讀較完全的隨機讀性能會高5倍以上。
另外4k的消息在完全隨機訪問情況下,仍然可以達到8K次每秒以上的讀性能。
由於Consume Queue存儲數據量極少,而且是順序讀,在PAGECACHE預讀作用下,Consume Queue的讀性能幾乎與內存一致,即使堆積情況下。所以可認為Consume Queue完全不會阻礙讀性能。
Commit Log中存儲了所有的元信息,包含消息體,類似於Mysql、Oracle的redolog,所以只要有Commit Log在,Consume Queue即使數據丟失,仍然可以恢復出來。
二、兩種刷盤策略
RocketMQ的所有消息都是持久化的,先寫入系統PAGECACHE,然後刷盤,可以保證內存與磁盤都有一份數據,訪問時,直接從內存讀取。

異步刷盤
技術分享圖片

在有RAID卡,SAS 15000轉磁盤測試順序寫文件,速度可以達到300M每秒左右,而線上的網卡一般都為千兆網卡,寫磁盤速度明顯快於數據網絡入口速度,那麽是否可以做到寫完內存就向用戶返回,由後臺線程刷盤呢?

由於磁盤速度大於網卡速度,那麽刷盤的進度肯定可以跟上消息的寫入速度。
萬一由於此時系統壓力過大,可能堆積消息,除了寫入IO,還有讀取IO,萬一出現磁盤讀取落後情況,會不會導致系統內存溢出,答案是否定的,原因如下:

寫入消息到PAGECACHE時,如果內存不足,則嘗試丟棄幹凈的PAGE,騰出內存供新消息使用,策略是LRU方式。
如果幹凈頁不足,此時寫入PAGECACHE會被阻塞,系統嘗試刷盤部分數據,大約每次嘗試32個PAGE,來找出更多幹凈PAGE。
綜上,內存溢出的情況不會出現。
同步刷盤
技術分享圖片

同步刷盤與異步刷盤的唯一區別是異步刷盤寫完PAGECACHE直接返回,而同步刷盤需要等待刷盤完成才返回,同步刷盤流程如下:

寫入PAGECACHE後,線程等待,通知刷盤線程刷盤。
刷盤線程刷盤後,喚醒前端等待線程,可能是一批線程。
前端等待線程向用戶返回成功。
三、多種消息查詢手段
豐富的消息查詢手段,幫助用戶快速定位消息,排查問題,RocketMQ支持按Message Id查詢、按Message Key查詢等。

按照Message Id查詢消息
技術分享圖片

如上圖所示,MsgId總共16字節,包含消息存儲主機地址,消息Commit Log offset。從MsgId中解析出Broker的地址和Commit Log的偏移地址,然後按照存儲格式所在位置消息buffer解析成一個完整的消息。

按照Message Key查詢消息
技術分享圖片

RocketMQ可以為每條消息指定Key,並根據建立高效的消息索引,索引邏輯結果如上圖所示,查詢過程如下:

根據查詢的key的hashcode%slotNum得到具體的槽的位置(slotNum是一個索引文件裏面包含的最大槽的數目,例如圖中所示slotNum=5000000)。
根據slotValue(slot位置對應的值)查找到索引項列表的最後一項(倒序排列,slotValue總是指向最新的一個索引項)。
遍歷索引項列表返回查詢時間範圍內的結果集(默認一次最大返回的32條記錄)
Hash沖突;尋找key的slot位置時相當於執行了兩次散列函數,一次key的hash,一次key的hash值取模,因此這裏存在兩次沖突的情況;第一種,key的hash值不同但模數相同,此時查詢的時候會在比較一次key的hash值(每個索引項保存了key的hash值),過濾掉hash值不相等的項。第二種,hash值相等但key不等,出於性能的考慮沖突的檢測放到客戶端處理(key的原始值是存儲在消息文件中的,避免對數據文件的解析),客戶端比較一次消息體的key是否相同。
存儲;為了節省空間索引項中存儲的時間是時間差值(存儲時間-開始時間,開始時間存儲在索引文件頭中),整個索引文件是定長的,結構也是固定的 。
四、消息過濾機制
RocketMQ的消息過濾方式有別於其他消息中間件,是在訂閱時,再做過濾,先來看下Consume Queue的存儲結構。

技術分享圖片

在Broker端進行Message Tag比對,先遍歷Consume Queue,如果存儲的Message Tag與訂閱的Message Tag不符合,則跳過,繼續比對下一個,符合則傳輸給Consumer。註意:Message Tag是字符串形式,Consume Queue中存儲的是其對應的hashcode,比對時也是比對hashcode。
Consumer收到過濾後的消息後,同樣也要執行在Broker端的操作,但是比對的是真實的Message Tag字符串,而不是Hashcode。
為什麽過濾要這樣做?

Message Tag存儲Hashcode,是為了在Consume Queue定長方式存儲,節約空間。
過濾過程中不會訪問Commit Log數據,可以保證堆積情況下也能高效過濾。
即使存在Hash沖突,也可以在Consumer端進行修正,保證萬無一失。
五、順序消息
很多業務有順序消息的需求,RocketMQ支持全局和局部的順序,一般推薦使用局部順序,將具有順序要求的一類消息hash到同一個隊列中便可保持有序,如下圖所示。

技術分享圖片

但順序消息,有自己的缺陷:

發送順序消息無法利用集群FailOver特性
消費順序消息的並行度依賴於隊列數量
隊列熱點問題,個別隊列由於哈希不均導致消息過多,消費速度跟不上,產生消息堆積問題
遇到消息失敗的消息,無法跳過,當前隊列消費暫停
目前,中間件團隊正在攻克這些缺陷,很快將出現在新特性當中。

六、事務消息
事務消息特性介紹參考Aliware MQ的文檔介紹。

七、定時消息
日常業務中有很多定時消息的場景,比如在電商交易中超時未支付關閉訂單的場景,在訂單創建時會發送一條 MQ 延時消息,這條消息將會在30分鐘以後投遞給消費者,消費者收到此消息後需要判斷對應的訂單是否已完成支付。如支付未完成,則關閉訂單,如已完成支付則忽略。

RocketMQ為了實現定時消息,引入延時級別,犧牲部分靈活性,事實上很少有業務需要隨意指定定時時間的靈活性。定時消息內容被存儲在數據文件中,索引按延時級別堆積在定時消息隊列中,具有跟普通消息一致的堆積能力,如下圖所示。

技術分享圖片

八、總結
以上為用戶比較關註的RocketMQ關鍵特性,RocketMQ中更多的技術將有專門的章節介紹,比如低延遲技術、高可用以及高可靠技術等。

RocketMQ 關鍵特性