天天看點

RocketMQ 關鍵特性

apache rocketmq之是以能在衆多的消息中間件中脫穎而出,能吸引數千企業使用者與rocketmq的關鍵特性是分不開的,本文詳細介紹rocketmq中的關鍵特性。

諸如kafka之類的消息中間件,在隊列數上升時性能會産生巨大的損失,rocketmq之是以能單機支援上萬的持久化隊列與其獨特的存儲結構分不開。

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,然後刷盤,可以保證記憶體與磁盤都有一份資料,通路時,直接從記憶體讀取。

RocketMQ 關鍵特性

在有raid卡,sas 15000轉磁盤測試順序寫檔案,速度可以達到300m每秒左右,而線上的網卡一般都為千兆網卡,寫磁盤速度明顯快于資料網絡入口速度,那麼是否可以做到寫完記憶體就向使用者傳回,由背景線程刷盤呢?

由于磁盤速度大于網卡速度,那麼刷盤的進度肯定可以跟上消息的寫入速度。

萬一由于此時系統壓力過大,可能堆積消息,除了寫入io,還有讀取io,萬一出現磁盤讀取落後情況,會不會導緻系統記憶體溢出,答案是否定的,原因如下:

寫入消息到pagecache時,如果記憶體不足,則嘗試丢棄幹淨的page,騰出記憶體供新消息使用,政策是lru方式。

如果幹淨頁不足,此時寫入pagecache會被阻塞,系統嘗試刷盤部分資料,大約每次嘗試32個page,來找出更多幹淨page。

綜上,記憶體溢出的情況不會出現。

RocketMQ 關鍵特性

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

寫入pagecache後,線程等待,通知刷盤線程刷盤。

刷盤線程刷盤後,喚醒前端等待線程,可能是一批線程。

前端等待線程向使用者傳回成功。

豐富的消息查詢手段,幫助使用者快速定位消息,排查問題,rocketmq支援按message id查詢、按message key查詢等。

RocketMQ 關鍵特性

如上圖所示,msgid總共16位元組,包含消息存儲主機位址,消息commit log offset。從msgid中解析出broker的位址和commit log的偏移位址,然後按照存儲格式所在位置消息buffer解析成一個完整的消息。

RocketMQ 關鍵特性

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的存儲結構。

RocketMQ 關鍵特性

在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到同一個隊列中便可保持有序,如下圖所示。

RocketMQ 關鍵特性

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

發送順序消息無法利用叢集failover特性

消費順序消息的并行度依賴于隊列數量

隊列熱點問題,個别隊列由于哈希不均導緻消息過多,消費速度跟不上,産生消息堆積問題

遇到消息失敗的消息,無法跳過,目前隊列消費暫停

目前,中間件團隊正在攻克這些缺陷,很快将出現在新特性當中。

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

rocketmq為了實作定時消息,引入延時級别,犧牲部分靈活性,事實上很少有業務需要随意指定定時時間的靈活性。定時消息内容被存儲在資料檔案中,索引按延時級别堆積在定時消息隊列中,具有跟普通消息一緻的堆積能力,如下圖所示。

RocketMQ 關鍵特性

以上為使用者比較關注的rocketmq關鍵特性,rocketmq中更多的技術将有專門的章節介紹,比如低延遲技術、高可用以及高可靠技術等。