RocketMQ在存儲(chǔ)架構(gòu)上的極致追求
內(nèi)容導(dǎo)讀:MQ作為一款中間件,就需要承載全公司所有業(yè)務(wù)系統(tǒng)使用需求,并高效穩(wěn)定運(yùn)行。因此,MQ對(duì)本身運(yùn)行效率有著非常苛刻的訴求。
為了實(shí)現(xiàn)高效率,其實(shí)需要很多方面一起配合來完成。比如存儲(chǔ)方式、內(nèi)存使用、負(fù)載均衡等等。
本文就RocketMQ為了實(shí)現(xiàn)高效的讀寫速率在存儲(chǔ)架構(gòu)上所做的努力,進(jìn)行下闡述。
?
Part one / 存儲(chǔ)結(jié)構(gòu)選型對(duì)比
為了更方便的進(jìn)行數(shù)據(jù)讀寫,消息在磁盤底層的文件目錄設(shè)計(jì),都需要關(guān)注和解決什么問題呢:
topic?,特別是允許消費(fèi)者按topic進(jìn)行接收。?再次,分布式集群下的多消費(fèi)者負(fù)載均衡。topic來拆分文件進(jìn)行存儲(chǔ),是否可以?topic都被寫入一個(gè)文件中,這樣,寫入時(shí),只要將消息按到達(dá)順序序追加到文件尾部即可,很容易實(shí)現(xiàn)順序?qū)懭搿?/span>topic拆開多文件存儲(chǔ),還是一整個(gè)文件存儲(chǔ)做有利有弊,需要按實(shí)際需要進(jìn)行權(quán)衡。?
Part two / RocketMQ的存儲(chǔ)方案選擇
RocketMQ存儲(chǔ)原始消息選擇的是寫同一個(gè)文件。
RocketMQ一般都是普通業(yè)務(wù)場(chǎng)景使用居多,生產(chǎn)者和topic眾多,如果都獨(dú)立開各自存儲(chǔ),每次消息生產(chǎn)的磁盤尋址對(duì)性能損耗是非常巨大的。kafka的文件存儲(chǔ)方式,是按topic拆分成partation來進(jìn)行的。是什么樣的原因,讓kafka做出了和RocketMQ相反的選擇呢?個(gè)人認(rèn)為,主要還是使用場(chǎng)景的區(qū)別,
kafka被優(yōu)先選擇用來進(jìn)行大數(shù)據(jù)處理,相對(duì)于業(yè)務(wù)場(chǎng)景,數(shù)據(jù)維度的topic要少很多,并且kafka的生產(chǎn)者(spark?flume?binlog等)機(jī)器會(huì)更加集中,這使得kafka選擇按topic拆分文件的缺陷不那么突出,而大數(shù)據(jù)處理更重要的是消息讀取,順序讀的優(yōu)勢(shì)得以被充分利用。
partation,單cunsumer的kafka,性能異常的優(yōu)秀" 是經(jīng)常被提及的一個(gè)觀點(diǎn),其原因,相信有了上面的分析應(yīng)該也差不多有結(jié)論了。?
?
Part three / RocketMQ怎樣平衡讀性能
RocketMQ為了保證消息寫入效率,在存儲(chǔ)結(jié)構(gòu)上選擇了順序?qū)?/span>,勢(shì)必會(huì)對(duì)消息的讀取和消費(fèi)帶來不便。commitLog中定位到所需消息的位置。索引最擅長(zhǎng)的事情么?所以,RocketMQ也為commitLog創(chuàng)建了索引文件,并且是區(qū)分topic的結(jié)構(gòu)。
RocketMQ 的消息體構(gòu)成

topic?是業(yè)務(wù)場(chǎng)景的唯一標(biāo)識(shí),不可缺少;?queueId?在申請(qǐng)topic的時(shí)候確定,關(guān)聯(lián)著消費(fèi)索引consumerQueue中的隊(duì)列ID;?tags?是消息特殊標(biāo)簽,用于業(yè)務(wù)系統(tǒng)訂閱時(shí)提前過濾(這個(gè)功能真的是太重要了,吃過苦的同學(xué)都清楚);?keys?是消息的關(guān)鍵字,構(gòu)建index索引,用于關(guān)鍵字查詢用;?msgBody?是真實(shí)消息體;commitLog里,消息一旦被寫入,是不可以更改順序和內(nèi)容的。commitLog規(guī)定最大1個(gè)G,達(dá)到規(guī)定大小則寫新的一個(gè)文件。索引結(jié)構(gòu)和構(gòu)建過程

consumerQueue?是一種機(jī)制,可以讓消費(fèi)端通過queue和commitLog之間的檢索關(guān)系,快速定位到commitLog里邊的具體消息內(nèi)容,然后拉取進(jìn)行消費(fèi)。consumerQueue?按?topic的不同,被分為不同的queue,根據(jù)queueId來被消費(fèi)者訂閱和消費(fèi);bytes的記錄,由消息在commitLog中的起始偏移量、消息體占用大小、type的hash碼三部分構(gòu)成。可以通過這三個(gè)部分快速定位到所需消息位置和類型。commitLog時(shí),專門的后臺(tái)服務(wù)--putMessageService,將索引信息分發(fā)到 consumerQueue 和index文件里,來構(gòu)建索引項(xiàng)。消息的消費(fèi)

CommitOffsetManager?會(huì)將當(dāng)前消費(fèi)者,針對(duì)topic的消費(fèi)位點(diǎn)進(jìn)行記錄,目的是讓下一個(gè)或者重新啟動(dòng)單餓消費(fèi)者記住這個(gè)消費(fèi)位點(diǎn),不至于重復(fù)消費(fèi)。
?
?
Part four / 讀效率的追求
commitLog的一部分文件交換到對(duì)外內(nèi)存。然后利用操作系統(tǒng)的pageCache技術(shù),在運(yùn)行過程中把內(nèi)存里的信息,與磁盤里的文件信息進(jìn)行同步,或者交換:
?
Part five / 總結(jié)
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號(hào)
好文章,我在看??
評(píng)論
圖片
表情
