開發與維運

RocketMQ高可用探究:消息存儲技術

本文始發於:雲棲社區
時間:2020-06-01
原文鏈接:https://yq.aliyun.com/articles/763218

RocketMQ是阿里開源的分佈式消息中間件,跟其它中間件相比,RocketMQ的特點是純JAVA實現、集群和HA實現相對簡單、在發生宕機和其它故障時消息丟失率更低,具有良好的高可用架構及穩定性。其發展的迭代歷史如下圖所示,從2007年至今已發展超過10年。
image.png

圖1:RocketMQ迭代歷史

1 消息存儲架構

image.png

圖2:RocketMQ消息存儲架構

RocketMQ的消息存儲架構如上圖所示,可以看到主要由三個跟消息存儲相關的文件構成。

  • CommitLog:消息及元數據的存儲主體。消息內容不是定長的,同時單個文件大小默認1G,文件名長度為20位,左邊補零,剩餘為起始偏移量。比如00000000000000000000代表了第一個文件,起始偏移量為0,文件大小為1G=1073741824;當第一個文件寫滿了,第二個文件為00000000001073741824,起始偏移量為1073741824,以此類推。消息主要是順序寫入日誌文件,當第一個文件滿了,再寫入下一個文件。
  • ConsumeQueue:消息消費隊列。RocketMQ是基於主題topic的訂閱模式,消息消費是針對主題進行的,如果要根據topic在commitlog文件中進行檢索消息,效率將會非常低效。ConsumeQueue(邏輯消費隊列)作為消費消息的索引,保存了指定Topic下的隊列消息在CommitLog中的起始物理偏移量offset、消息大小size和消息Tag的HashCode值。所以ConsumeQueue文件可以看成是基於topic的CommitLog索引文件。ConsumeQueue文件夾的組織方式如下:topic/queue/file三層組織結構。而ConsumeQueue存儲路徑為:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。與Commitlog一樣,ConsumeQueue文件採取了定長設計,單個文件由30W個條目組成,每一個條目共20個字節,分別為8字節的CommitLog物理偏移量、4字節的消息長度、8字節tag hashcode,Comsumer可以像數組一樣隨機訪問每一個條目,每個ConsumeQueue文件大小約5.72M。
  • IndexFile:IndexFile(索引文件)提供了一種可以通過key或時間區間來查詢消息的方法。Index文件的存儲位置是:$HOME /store/index${fileName},文件名fileName是以創建時的時間戳命名的,固定的單個IndexFile文件大小約為400M,一個IndexFile可以保存2000W個索引。

2 頁緩存與內存映射

頁緩存(PageCache)是OS對文件的緩存,用於加速對文件的讀寫。一般來說,程序對文件進行順序讀寫的速度幾乎接近於內存的讀寫速度,主要原因就是由於OS使用PageCache機制對讀寫訪問操作進行了性能優化,將一部分的內存用作PageCache。對於數據的寫入,OS會先寫入至Cache內,隨後通過異步的方式由pdflush內核線程將Cache內的數據刷盤至物理磁盤上。對於數據的讀取,如果一次讀取文件時出現未命中PageCache的情況,OS從物理磁盤上訪問讀取文件的同時,會順序對其他相鄰塊的數據文件進行預讀取。

在RocketMQ中,ConsumeQueue邏輯消費隊列存儲的數據較少,並且是順序讀取,在page cache機制的預讀取作用下,Consume Queue文件的讀性能幾乎接近讀內存,即使在有消息堆積情況下也不會影響性能。而對於CommitLog消息存儲的日誌數據文件來說,讀取消息內容時會產生較多的隨機訪問讀取,嚴重影響性能。如果選擇合適的系統IO調度算法,比如設置調度算法為“Deadline”(此時塊存儲採用SSD的話),隨機讀的性能也會有所提升。

另外,RocketMQ主要通過MappedByteBuffer對文件進行讀寫操作。其中,利用了NIO中的FileChannel模型將磁盤上的物理文件直接映射到用戶態的內存地址中(這種Mmap的方式減少了傳統IO將磁盤文件數據在操作系統內核地址空間的緩衝區和用戶應用程序地址空間的緩衝區之間來回進行拷貝的性能開銷),將對文件的操作轉化為直接對內存地址進行操作,從而極大地提高了文件的讀寫效率(正因為需要使用內存映射機制,故RocketMQ的文件存儲都使用定長結構來存儲,方便一次將整個文件映射至內存)。

3 消息刷盤

image.png

圖3:消息刷盤機制

  • 同步刷盤:如上圖所示,當消息真正持久化至磁盤後,RocketMQ的Broker端才會真正返回給Producer端一個成功的ACK響應。同步刷盤對MQ消息可靠性來說是一種不錯的保障,但是性能上會有較大影響,一般適用於金融業務應用。
  • 異步刷盤:能夠充分利用OS的PageCache的優勢,只要消息寫入PageCache即可將成功的ACK返回給Producer端。消息刷盤採用後臺異步線程提交的方式進行,降低了讀寫延遲,提高了MQ的性能和吞吐量。

高鑫

阿里雲智能GTS-SRE團隊高級技術支持工程師

多年雲計算、金融行業應用運維工作經驗,銀行核心應用運維架構設計與應用維護。專注中間件及數據庫技術,現就職於阿里雲智能GTS-SRE團隊,主要負責中間件領域客戶技術支持工作。

我們是阿里雲智能全球技術服務-SRE團隊,我們致力成為一個以技術為基礎、面向服務、保障業務系統高可用的工程師團隊;提供專業、體系化的SRE服務,幫助廣大客戶更好地使用雲、基於雲構建更加穩定可靠的業務系統,提升業務穩定性。我們期望能夠分享更多幫助企業客戶上雲、用好雲,讓客戶雲上業務運行更加穩定可靠的技術,您可用釘釘掃描下方二維碼,加入阿里雲SRE技術學院釘釘圈子,和更多雲上人交流關於雲平臺的那些事。
image.png

Leave a Reply

Your email address will not be published. Required fields are marked *