MQ初窺門徑【面試必看的Kafka和RocketMQ存儲區別】

 

MQ初窺門徑

全稱(message queue)消息隊列,一個用於接收消息、存儲消息並轉發消息的中間件linux

應用場景

用於解決的場景,總之是能接收消息並轉發消息緩存

  1. 用於異步處理,好比A服務作了什麼事情,異步發送一個消息給其餘B服務。
  2. 用於削峯,有些服務(秒殺),請求量很高,服務處理不過來,那麼請求先放到消息隊列裏面,後面按照能力處理,至關於蓄水池。
  3. 應用解耦、消息通信等等

總之MQ是能夠存放消息並轉發消息的中間件,場景取決於拿這個能力去解決什麼問題網絡

MQ概念模型

MQ向別人承諾的場景是接收消息,存儲,並能夠轉發消息異步

接收消息

接收消息,那麼接收誰的消息,爲了說明這個問題,那麼mq須要引入一個概念,叫作生產者,也就是發送消息的服務,不然沒有辦法來區分是誰發的消息,生產者經過網絡發送消息就能夠,中間的細節咱們先不探討。
那麼還有一個問題就是消息發送給誰?性能

  1. 我在發送消息的時候,指明我要發送給誰,就像發送短信同樣,你須要指明你要發送給誰?
    這種方案在使用中是有問題的,由於在如今業務不少場景中, 發送方其實根本不知道對方是誰,他只是將本身的狀態發送出來,那麼誰須要這個消息,誰就接收,第二個若是指明瞭接收方,那麼之後增長一個接收方就要改一下配置或者代碼,將發送消息的人跟接收消息的人綁定在一塊兒了
    那麼有沒有方案,解耦的最好辦法就是中間人,也叫中間層,我只發送給第三方,誰要消息,問第三方要,那麼至關於我把發送的目標改成發送給第三方,這裏的第三方就是mq,爲了說明說明發送的地方,mq引入了topic的概念,發送方把消息發送到mq指定的一個通道中,之後誰想要這個消息,就跟mq說我想要這個通道的消息,也就是發送方發送的消息。

消費消息

消費消息,那麼同理的一個問題,誰消費消息,爲了說明那麼mq須要引入一個概念,叫作消費者,也就是消費消息的服務,不然沒有辦法來區分是誰在接收消息,消費者經過網絡接收消息就能夠了,中間的細節咱們先不探討。google

那麼問題來了,消費者怎麼說明消費誰的消息,上文已經說了,經過指明mq的topic,來決定我要哪一類消息。spa

至此咱們總結一下最後的模型3d

 

也就是最後生產者和消費者經過MQ的topic概念來實現解耦。code

存儲

說到存儲,其實效率纔是最主要的,容量不是咱們關心的,可是說到存儲,不僅是mq,全部須要高效率的存儲其實最後利用的核心都是同樣的。中間件

  1. 隨機寫轉換成順序寫
  2. 集中刷盤
爲何隨機寫要轉換爲順序寫?

第一 如今主流的硬盤是機械硬盤
第二 機械硬盤的機械結構一次讀寫時間 = 尋道時間 + 旋轉延遲 + 讀取數據時間
那麼尋道時間比較長,若是是順序寫,只須要一次尋道時間,關於機械硬盤整個過程,讀者可自行google。

爲何集中刷盤?

由於每次刷盤都會進行系統調用,第二仍是跟硬盤的自己屬性有關,不管是機械硬盤仍是ssd按照必定塊刷盤會比小數據刷盤效率更好

kafka

爲何先說kafka的存儲,由於kafka是第一個高性能的消息中間件,其中rocketmq也是借鑑於它,因此咱們先說。

 

先給出最終模型變化圖。

  1. 爲何引入消費組概念?
    上一次模型圖咱們尚未消費組,那麼引入消費組,是由於如今一個服務都有不少實例在運行,消費組是對這羣一羣機器的一個劃分,他仍是一個概念而已。
  2. mq內部也發生了變化,一個topic後面又對應了不少partition,partition也是一個概念,他只不過是把一個topic分紅了不少份,每一份叫一個partition,你高興也能夠叫他xxx,那麼咱們來講說爲何要分紅不少份,一份不行嗎?
    由於如今一個服務有不少實例在運行,若是topic只有一份的話,那麼全部的實例都會來消費消息,而且都是搶佔咱們一個topic,這不可避免引入了多實例競爭,以及他們之間怎麼協調,一堆問題須要關注解決,如今我把topic分紅了不少份,每一份只給一個實例,那麼就不會引入各實例之間的競爭問題了,簡化了mq的問題。
  3. 生產組的引入也是同樣的,只不過是一組機器的一個概念,一個邏輯的劃分,生產者發送消息原先是發往topic,那麼如今topic分紅了不少份,生產者發送消息,須要說明發往哪一個partition或者隨意分配均可以,只不過最終發送的消息,會到一個topic下的一份裏面。不管使用哪一種映射方式均可以。

那麼模型出來了,咱們說說存儲的問題。
對於kafka,一個partition對應一個文件,每次消息來都是順序寫這個文件。而且是定時刷盤,而不是每次寫都刷盤,因此kafka的寫很是高效。

rocketmq

 

上文咱們說了rocketmq借鑑於kafka,因此存儲借鑑了kafka,可是rocketmq不是僅僅把partition改爲了ConsumeQueue,在這裏作了變化,原先kafka,裏面partition存儲的是整個消息,可是如今ConsumeQueue裏面是存儲消息的存儲地址,可是不存儲消息了。

 

如今每一個ConsumeQueue存儲的是每一個消息在commitlog這個文件的地址,可是消息存在於commitlog中。
也就是全部的消息體都寫在了一個文件裏面,每一個ConsumeQueue只是存儲這個消息在commitlog中地址。

存儲對比

  1. 消息體存儲的變化
    那麼咱們先來看看kafka,假設partition有1000個,一個partition是順序寫一個文件,整體上就是1000個文件的順序寫,是否是就變成了隨機寫,因此當partition增長到必定數目後,kafka性能就會降低。而rocketmq是把消息都寫到一個CommitLog文件中,因此至關於一個文件的順序寫。

  2. 爲何索引文件(ConsumeQueue)的增長對性能影響沒有那麼partition大?
    (kafka也有索引文件,在這裏只是想說明索引文件的增長跟partition增長的區別)
    雖然rocketmq是把消息都寫到一個CommitLog文件中,可是按照上面的實例會有1000個ConsumeQueue,也就是一千個文件,那麼爲何就沒有把順序寫變成隨機寫,帶來性能的降低呢?首先就要介紹linux的pagecache

     

    咱們日常調用write或者fwrite的時候,數據尚未寫到磁盤上,只是寫到一個內核的緩存(pagecache),只有當咱們主動調用flush的時候纔會寫到硬盤中。或者須要回寫的pagecache佔總內存必定比例的時候或者一個應該回寫的page超過必定時間尚未寫磁盤的時候,內核會將這些數據經過後臺進程寫到磁盤中(總結就是達到必定比例,或者多長時間尚未回寫,會被內核自動回寫)。

    而後咱們如今來看看爲何大量索引文件的順序寫沒有像partition同樣致使性能明顯降低。ConsumeQueue只存儲了(CommitLog Offet + Size + Message Tag Hashcode),一共20個字節,那麼當commitlog定時任務刷盤以後,應該回寫的pagecache的比例就會降低不少,那麼ConsumeQueue的部分能夠不用刷盤,就至關於ConsumeQueue的內容會等待比較長的時間聚合批量寫入,而kafka每一個partition都是存儲的消息體,由於消息體都相對較大,基本在kb之上。
    當一個partition刷盤的時候,應該回寫的pagecache的比例下降的並很少,不能阻止其餘partition的刷盤,因此會大量存在多個partition同時刷盤的場景,變成隨機寫。可是rocketmq消息都會寫入一個commitLog,也就是順序寫。

因此咱們總結下這個點:

一、consumerQueue消息格式大小固定(20字節),寫入pagecache以後被觸發刷盤頻率相對較低。就是由於每次寫入的消息小,形成他佔用的pagecache少,主要佔用方一旦被清理,那麼他就能夠不用清理了。

二、kafka中多partition會存在隨機寫的可能性,partition之間刷盤的衝撞率會高,可是rocketmq中commitLog都是順序寫。

 

  歡迎關注博主公衆號,後面會持續更新mq系列知識點,一塊兒討論。

       

相關文章
相關標籤/搜索