<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          RocketMQ在存儲(chǔ)架構(gòu)上的極致追求

          共 2962字,需瀏覽 6分鐘

           ·

          2022-02-26 12:56

          內(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)注和解決什么問題呢:


          ?首先,最基本的,消息原始記錄的寫入和存儲(chǔ),且速率要快。?其次,要可以區(qū)分topic?,特別是允許消費(fèi)者按topic進(jìn)行接收。?再次,分布式集群下的多消費(fèi)者負(fù)載均衡。


          那么問題來了,消息文件該怎么設(shè)計(jì)呢?
          如果按topic來拆分文件進(jìn)行存儲(chǔ),是否可以?


          ?缺點(diǎn):生產(chǎn)者寫入時(shí)選擇對(duì)應(yīng)的文件來寫入。當(dāng)數(shù)據(jù)量逐漸增大之后,定位查詢文件地址,對(duì)磁盤的尋址所帶來的性能損耗,將不再可以忽略。?優(yōu)點(diǎn):在消費(fèi)時(shí),可以直接加載相關(guān)文件進(jìn)行讀取,不會(huì)產(chǎn)生隨機(jī)尋址。


          如果用一整個(gè)文件來存消息呢?
          ?
          優(yōu)點(diǎn):所有的topic都被寫入一個(gè)文件中,這樣,寫入時(shí),只要將消息按到達(dá)順序序追加到文件尾部即可,很容易實(shí)現(xiàn)順序?qū)懭搿?/span>
          ?
          缺點(diǎn):消費(fèi)時(shí),需要根據(jù)輔助信息來在文件中定位消息,會(huì)產(chǎn)生隨機(jī)讀,損耗性能。
          因此,不管是按topic拆開多文件存儲(chǔ),還是一整個(gè)文件存儲(chǔ)做有利有弊,需要按實(shí)際需要進(jìn)行權(quán)衡。

          ?

          Part two / RocketMQ的存儲(chǔ)方案選擇

          RocketMQ存儲(chǔ)原始消息選擇的是寫同一個(gè)文件。
          生產(chǎn)者將消息順序?qū)懭隿ommitLog文件
          究其原因,是由于RocketMQ一般都是普通業(yè)務(wù)場(chǎng)景使用居多,生產(chǎn)者和topic眾多,如果都獨(dú)立開各自存儲(chǔ),每次消息生產(chǎn)的磁盤尋址對(duì)性能損耗是非常巨大的。
          旁證側(cè)引:
          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,單cunsumerkafka,性能異常的優(yōu)秀" 是經(jīng)常被提及的一個(gè)觀點(diǎn),其原因,相信有了上面的分析應(yīng)該也差不多有結(jié)論了。

          ?

          ?

          Part three / RocketMQ怎樣平衡讀性能

          從第一部分的存儲(chǔ)方案對(duì)比可以知道,RocketMQ為了保證消息寫入效率,在存儲(chǔ)結(jié)構(gòu)上選擇了順序?qū)?/span>,勢(shì)必會(huì)對(duì)消息的讀取和消費(fèi)帶來不便。
          那么,它是怎么來平衡消費(fèi)時(shí)的讀取速率的呢?
          關(guān)鍵問題是,找到一種途徑,可以快速的在commitLog中定位到所需消息的位置。
          從一堆數(shù)據(jù)中,快速定位想要的數(shù)據(jù),這不是索引最擅長(zhǎng)的事情么?所以,RocketMQ也為commitLog創(chuàng)建了索引文件,并且是區(qū)分topic的結(jié)構(gòu)。
          存儲(chǔ)架構(gòu)和存儲(chǔ)構(gòu)建鏈路示意圖

          RocketMQ 的消息體構(gòu)成

          消息體元素構(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í)消息體;


          消息由發(fā)布者發(fā)布,并依次的、順序的寫到commitLog里,消息一旦被寫入,是不可以更改順序和內(nèi)容的。commitLog規(guī)定最大1個(gè)G,達(dá)到規(guī)定大小則寫新的一個(gè)文件。

          索引結(jié)構(gòu)和構(gòu)建過程

          consumerQueue結(jié)構(gòu)和創(chuàng)建過程
          consumerQueue?是一種機(jī)制,可以讓消費(fèi)端通過queuecommitLog之間的檢索關(guān)系,快速定位到commitLog里邊的具體消息內(nèi)容,然后拉取進(jìn)行消費(fèi)。
          consumerQueue?按?topic的不同,被分為不同的queue,根據(jù)queueId來被消費(fèi)者訂閱和消費(fèi);
          其中每個(gè)索引項(xiàng)是一個(gè)固定大小為20bytes的記錄,由消息在commitLog中的起始偏移量、消息體占用大小、typehash碼三部分構(gòu)成。可以通過這三個(gè)部分快速定位到所需消息位置和類型。
          而上述索引的構(gòu)建過程,是在消息被寫入commitLog時(shí),專門的后臺(tái)服務(wù)--putMessageService,將索引信息分發(fā)到 consumerQueue 和index文件里,來構(gòu)建索引項(xiàng)。
          建索引的過程,實(shí)際上是一種分而治之思維的落地,除了索引,還有redis中的各種指標(biāo)維護(hù),核心是 分散壓力到每次請(qǐng)求,避免了大規(guī)模集中計(jì)算。

          消息的消費(fèi)

          消費(fèi)者對(duì)應(yīng)consumerQueue不一定是一對(duì)一的,因此,怎么來讓每個(gè)新的消費(fèi)者來了不會(huì)重復(fù)消費(fèi)呢?
          offset消費(fèi)位點(diǎn)記錄
          在消息成功被拉取并消費(fèi)時(shí),后臺(tái)任務(wù)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)。
          因此,整個(gè)文件目錄就一目了然了:

          ?

          ?

          Part four / 讀效率的追求

          雖然通過上述文件存儲(chǔ)結(jié)構(gòu)的分析,我們知道,消費(fèi)者可以根據(jù)索引文件中的索引項(xiàng)來快速定位, 但事實(shí)上,消息的發(fā)布和消費(fèi),不可能直接針對(duì)磁盤進(jìn)行讀寫操作的,這樣效率會(huì)非常非常低。
          實(shí)際上,我們的操作基本是針對(duì)一塊內(nèi)存進(jìn)行操作的?。
          利用NIO的內(nèi)存映射機(jī)制,我們將commitLog的一部分文件交換到對(duì)外內(nèi)存。然后利用操作系統(tǒng)pageCache技術(shù),在運(yùn)行過程中把內(nèi)存里的信息,與磁盤里的文件信息進(jìn)行同步,或者交換:


          ?消息發(fā)布者,在發(fā)布消息的時(shí)候,首先把消息添加到內(nèi)存里,然后根據(jù)刷盤的配置可以來指定是同步刷盤還是異步刷盤,來將內(nèi)存中的數(shù)據(jù)同步到磁盤上。?消息的消費(fèi)者,在消費(fèi)消息的時(shí)候,大多數(shù)情況下,會(huì)直接命中到內(nèi)存上,不會(huì)進(jìn)行磁盤讀,但極個(gè)別的情況下,需要消費(fèi)的消息,在內(nèi)存中沒法找到,這時(shí)候,就需要用換頁(yè)技術(shù),將相關(guān)的信息,拉取到內(nèi)存中。為什么是相關(guān)信息,而不是需要什么拉取什么?這是有一個(gè)機(jī)制,來保證潛在的即將被消費(fèi)的信息直接換入內(nèi)存,來提交效率。


          摘自:Qcon大會(huì) RocketMQ分享資料

          ?

          Part five / 總結(jié)

          整體一套處理流程看下來,其實(shí)我們可以看到很多熟悉的身影,比如Mysql的索引,redis的統(tǒng)計(jì)信息記錄等等,都非常相似。
          其實(shí),我們可以這么認(rèn)為:對(duì)于信息存儲(chǔ)和查詢的處理方案大都如出一轍,只要把握住最核心的部分,然后根據(jù)實(shí)際業(yè)務(wù)訴求進(jìn)行適配優(yōu)化,基本都是可以達(dá)到期望的結(jié)果的。

          有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號(hào)


          好文章,我在看??

          瀏覽 20
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人操在线播放 | 二区三区导航 | 国产亚洲精品久久777777 | 亚洲娇小wite黑人粗大 | 青青草在线视频免费视频 |