2018-04-13開源消息隊列RocketMQ

核心組件(4個組件+消息存儲結構)

客戶端消費模式

1. MQ的使用場景

昨天在寫完之后,有些讀者在評論中提出:到底什么時候用MQ?舉幾個典型的例子。

1.最常用的就是Publish/Subscribe

從開發(fā)模式中,我們使用過JAVA Swing Button EventListener或者使用JQuery 的自定義事件,由某個動作觸發(fā)然后傳播下去,這個事件廣播就是Publish,我們監(jiān)聽的過程,就是訂閱的過程。這個是MQ最基本的功能。

舉個業(yè)務例子:訂單支付的過程,會牽扯到會員模塊、消息短信推送服務、訂單更新,最初我們還能想清楚,所有把代碼寫在一個service中,但后續(xù)逐漸加業(yè)務,例如:會員還需要有積分、會員余額要預警、會員等級也得變化、還得告訴商戶費用結清了。我舉的例子可能不是很恰當,但這確實反映問題了,系統(tǒng)過于耦合。從這里引入MQ,對周邊業(yè)務做清理,附屬業(yè)務直接訂閱消息。

2.應對大流量沖擊、削峰填谷

系統(tǒng)的前端數據采集設備數量太大,且具有業(yè)務波峰。還有就是典型的雙11,促銷活動會期間,系統(tǒng)會承受比正常流量高很多倍的沖擊。此時,采集的數據不要求實時,購物訂單也不需要實時反饋給用戶,面對這種應用場景,使用消息隊列可以實現消息的異步處理、請求的流量削峰作用。

3.異構系統(tǒng)的通信

直接上例子:一個系統(tǒng)有多個開發(fā)小組,由于功能特點分別使用了C++、JAVA、Go、python等多種語言實現,系統(tǒng)之間需要通信,使用事件驅動架構,那么這里可以引入MQ了,作為異構系統(tǒng)和事件驅動架構的backbone。

2. RocketMQ初識

首先應該看看RocketMQ的收發(fā)消息模型:

消息收發(fā)模型

上圖,你能看出來:

生產者生產消息,可以放到隊列中,多個消費者可以消費。

生產者的同一種消息,可以放到不同的隊列中,由消費者消息。

實際部署圖:

物理部署圖

如上圖所示, RocketMQ的部署結構有4部分組成:NameServer、Broker、Producer、Consumer。

這里我們抽象以下:分為客戶端(Producer、Consumer)、服務端(Broker、NameServer)兩部分。簡單來說,就是客戶端的收發(fā)消息、服務器接收消息并持久化。

2.1 客戶端的功能有什么?

client發(fā)送消息有負載均衡,因為客戶端中保存著當前所有服務器列表,每次發(fā)送都切換一臺服務器發(fā)送消息,服務均衡負載。

發(fā)送代碼為線程安全,支持高并發(fā)寫操作。

消費者端負載均衡集群消費模式下,同一個ID的所有消費者實例平均消費該Topic的所有隊列。這里要注意,廣播模式下,則一個consumer實例消費這個Topic對應的所有隊列。這點和Apache ActiveMQ的主題訂閱是一樣的,每個消費者消費Topic的所有內容(若存在消費者消費慢,容易內存溢出)。

2.2 服務端(Broker)的功能有什么?

Broker就是RocketMQ的核心了,RocketMQ高并發(fā)讀寫主要利用Linux操作系統(tǒng)的PageCache特性,通過Java的MappedByteBuffer直接操作PageCache。MappedByteBuffer能直接將文件直接映射到內存,其實就是Map把文件的內容被映像到計算機虛擬內存的一塊區(qū)域,這樣就可以直接操作內存當中的數據而無需操作的時候每次都通過I/O去物理硬盤寫文件的。

鼓勵下自己,下面開始深入了

2.3 服務端存儲的消息結構

RocketMQ高性能讀寫,得益于它的消息存儲結構:commitLog和comsume queue兩部分組成。(這部分大概了解,比別人多掌握一點)

數據存儲結構

commitLog

保存所有消息的元數據,所有消息到達Broker之后都會保存到commitLog中,所有的topic消息都會寫入一個commitLog中,通常的目錄是:

${user.home}/store/${commitlog}/${fileName}

每個commitLog上限是1G,滿了之后會自動新建一個commitLog文件保存數據。這里的Log文件會有清理機制,有兩種清理措施:

按時間清理,默認清理3天前的commitLog。

按磁盤占比,當磁盤使用到75%,開始清理最老的commmitLog文件。

consumeQueue

ConsumeQueue相當于CommitLog的索引文件,消費者消費時會從consumeQueue中查找消息在commitLog中的offset,再去commitLog中查找元數據。如果某個消息只在CommitLog中有數據,沒在ConsumerQueue中, 則消費者無法消費

consumeQueue的數據結構包含3個部分: consumeQueue中存儲單元是一個20字節(jié)定長的二進制數據,順序寫順序讀 。如圖:

ConsumeQueue存儲格式的特性,保證了寫過程的順序寫盤(寫CommitLog文件),在單臺服務器上,MQ元數據都落在單個文件上,大量數據IO都在順序寫同一個commitLog,滿1G了再寫新的,真正意義上的順序寫盤,再加上MQ默認是累計4K才強制從PageCache中刷到磁盤(緩存),所以高并發(fā)寫性能突出,至于讀取數據呢,有Queue的offset、size,讓讀盤操作是跳躍式的。在RocketMQ中讀取消息是依賴系統(tǒng)PageCache,PageCache命中率越高,讀性能越高,Linux平時也會盡量預讀數據,使得應用直接訪問磁盤的概率降低。

3. 核心組件

3.1 Name Server

用于存儲Topic、Broker的關系,簡單、穩(wěn)定。多個Name-Server之間不通信,單臺NameServer宕機不影響其他NameServer與集群;及時整個NameServer集群宕機,已經正常工作的Producer、Consumer、Broker仍然正常工作,但新加入的就無法工作。

NameServer壓力不會大,主要用戶維持心跳和提供Toptic-Broker的關系。但如過topic過多,導致Nameserver心跳數據過大,網絡不好的情況下,傳輸失敗,導致NameServer誤認為Broker心跳失敗。

3.2 Broker

Broker部署相對復雜:

Broker分為Master與Slave,一個Master可以對應多個Slave,但是一個Slave只能對應一個Master。

Master與Slave的對應關系通過指定相同的BrokerName,不同的BrokerId來定義。(BrokerId為0表示Master,非0表示Slave)

Master也可以部署多個,每個Broker與Name Server集群中的所有節(jié)點建立長連接,定時注冊Topic信息到所有Name Server。

具有高并發(fā)讀寫,負載均衡和動態(tài)伸縮的特性:

負載均衡:Broker上存Topic信息,Topic由多個隊列組成,隊列會平均分散在多個Broker上,而Producer的發(fā)送機制保證消息盡量平均分布到所有隊列中,最終效果就是所有消息都平均落在每個Broker上。

動態(tài)伸縮:Topic維度:假如一個Topic的消息量特別大,但集群水位壓力還是很低,就可以擴大該Topic的隊列數,Topic的隊列數跟發(fā)送、消費速度成正比。Broker維度:如果集群水位很高了,需要擴容,直接加機器部署B(yǎng)roker就可以。Broker起來后想Namesrv注冊,Producer、Consumer通過Namesrv發(fā)現新Broker,立即跟該Broker直連,收發(fā)消息。

高可用與可靠性

高可用:Broker提供主備,主宕機,備提供讀,不可寫。

可靠性:所有發(fā)送到Broker的消息,有同步刷盤和異步刷盤。同步刷盤時,消息寫入物理文件才會返回成功。異步刷盤時,只有機器宕機,才會產生消息丟失,broker掛掉可能會發(fā)生,但是機器宕機崩潰是很少發(fā)生的,除非突然斷電。

Broker與NameServer的心跳 ,單個Broker跟所有Namesrv保持心跳請求,心跳間隔為30秒,心跳請求中包括當前Broker所有的Topic信息。Namesrv會反查Broer的心跳信息,如果某個Broker在2分鐘之內都沒有心跳,則認為該Broker下線,調整Topic跟Broker的對應關系。但此時Namesrv不會主動通知Producer、Consumer有Broker宕機。

3.3 Producer

Producer啟動時,也需要指定Namesrv的地址,從Namesrv集群中隨機選一臺建立長連接。如果該Namesrver宕機,會自動連其他Nameserver。直到有可用的Namesrv為止。

Producer每30秒從Namesrv獲取Topic跟Broker的映射關系,更新到本地內存中。Producer會和它要發(fā)送的topic相關的master類型的broker建立TCP連接,每隔30秒發(fā)一次心跳。在Broker端也會每10秒掃描一次當前注冊的Producer,如果發(fā)現某個Producer超過2分鐘都沒有發(fā)心跳,則斷開連接。

生產者發(fā)送時,會自動輪詢當前所有可發(fā)送的broker,一條消息發(fā)送成功,下次換另外一個broker發(fā)送,以達到消息平均落到所有的broker上。

3.4 Consumer

消費者啟動時需要指定Namesrv地址,隨機與其中一個Namesrv建立長連接。消費者每隔30秒從nameserver獲取所有topic的最新隊列情況(這意味著某個broker如果宕機,客戶端最多要30秒才能感知),并向提供Topic服務的Master、Slave建立長連接,且定時向Master、Slave發(fā)送心跳。Consumer既可以從Master訂閱消息,也可以從Slave訂閱消息,訂閱規(guī)則由Broker配置決定。

Consumer跟Broker是長連接,會每隔30秒發(fā)心跳信息到Broker。Broker端每10秒檢查一次當前存活的Consumer,若發(fā)現某個Consumer 2分鐘內沒有心跳,就斷開與該Consumer的連接,并且向該消費組的其他實例發(fā)送通知,觸發(fā)該消費者集群的負載均衡。

3.5 通信關系

Producer和Name Server:每一個Producer會與Name Server集群中的一臺機器建立TCP連接,會從這臺Name Server上拉取路由信息。

Producer和broker:Producer會和它要發(fā)送的topic相關的master類型的broker建立TCP連接,用于發(fā)送消息以及定時的心跳信息。broker中會記錄該Producer的信息,供查詢使用

broker與Name Server:broker(不管是master還是slave)會和每一臺Name Server機器來建立TCP連接。broker在啟動的時候會注冊自己配置的topic信息到Name Server集群的每一臺機器中。即每一臺Name Server都有該broker的topic的配置信息。master與master之間無連接,master與slave之間有連接

Consumer和Name Server:每一個Consumer會和Name Server集群中的一臺機器建立TCP連接,會從這臺Name Server上拉取路由信息,進行負載均衡

Consumer和broker:Consumer可以與master或者slave的broker建立TCP連接來進行消費消息,Consumer也會向它所消費的broker發(fā)送心跳信息,供broker記錄。

4. 消費模式

4.1 消費者端的消費以及負載均衡

先討論消費者的消費模式,消費者有兩種模式消費:

集群消費

廣播消費。

廣播消費

每個消費者消費Topic下的所有隊列。

集群消費

一個topic可以由同一個ID下所有消費者分擔消費。具體例子:假如TopicA有6個隊列(kafka稱之為分區(qū)),某個消費者ID起了2個消費者實例,那么每個消費者負責消費3個隊列。如果再增加一個消費者ID相同消費者實例,即當前共有3個消費者同時消費6個隊列,那每個消費者負責2個隊列的消費。

消費者端的負載均衡,就是集群消費模式下,同一個ID的所有消費者實例平均消費該Topic的所有隊列。

對于負載均衡,在出現分區(qū)(kafka稱為分區(qū))或者隊列(RocketMQ稱隊列)增加或者減少的時候、Consumer增加或者減少的時候都會進行reblance操作。

對于RocketMQ:客戶端自己會定時對所有的topic的進行reblance操作,對于每個topic,會從broker獲取所有Consumer列表,從broker獲取隊列列表,按照負載均衡策略,計算各自負責哪些隊列。這種就要求進行負載均衡的時候,各個Consumer獲取的數據是一致的,不然不同的Consumer的reblance結果就不同。

遍歷Consumer下的所有topic,然后根據topic訂閱所有的消息

獲取同一topic和Consumer Group下的所有Consumer

然后根據具體的分配策略來分配消費隊列,分配的策略包含:平均分配、消費端配置等

4.2 客戶端消費是broker的Push還是客戶端的Pull?

實際上consumer的消費只有主動拉的方式,那么有沒有push的方式?實際上類似http的請求長連接,consumer發(fā)起pull請求,等待broker有數據之后,再把數據push回來,實際上還是主動拉消息。

以上我們對Rocket做了全面的理論探索,后續(xù)配合實施外加代碼實戰(zhàn),帶你玩轉RocketMQ。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容