一、消息隊列基本概念
1.消息隊列概述
消息隊列,一般我們會簡稱它為MQ(Message Queue),消息隊列中間件是分布式系統中重要的組件,主要解決應用解耦,異步消息,流量削鋒等問題,實現高性能,高可用,可伸縮和最終一致性架構。目前使用較多的消息隊列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ
消息隊列可以簡單理解為:把要傳輸的數據放在隊列中。
把數據放到消息隊列叫做生產者
從消息隊列里邊取數據叫做消費者
2.消息隊列背景(舉例)
周日玩手機,某東網APP突然蹦出來一條通知“為了回饋老客戶,牛奶買一送一,活動僅限今天!”。買一送一還有這種好事,沒忍住立馬點了進去。立馬買了兩箱,下單、支付一氣呵成!
第二天正常上着班,突然接到快遞小哥的電話:
小哥:“你是xx嗎?你的快遞到了,我現在在你樓下,你來拿一下吧!”。
我:“這……我在上班呢,可以晚上送過來嗎?“。
小哥:“晚上可不行哦,晚上我也下班了呢!”。
於是兩個人僵持了很久……
最后小哥說,要不我幫你放到樓下便利店吧,你晚上下班了過來拿,尷尬的局面這才得以緩解!
回到正題,如果沒有便利店,那快遞小哥和我的交互圖就應該如下:

會出現什么情況呢?
1、為了這幾箱牛奶,我請假回去拿,一天工資沒了,還扣年假,血虧。
2、小哥一直在你樓下等,小哥還有其他的快遞要送,也不可能等你的。
3、周末再送,等着喝牛奶呢,怎么可能。
4、這個牛奶我不要了,可能嗎!
便利店出現后,交互圖就應如下:

3.消息隊列應用場景
在上面例子中,“快遞小哥”和“我”就是需要交互的兩個系統,便利店就是我們本文要講的-“消息中間件”。總結下來小芳便利店(消息中間件)出現后有如下好處:
1、 解耦
快遞小哥手上有很多快遞需要送,他每次都需要先電話一一確認收貨人是否有空、哪個時間段有空,然后再確定好送貨的方案。這樣完全依賴收貨人了!如果快遞一多,快遞小哥估計的忙瘋了……如果有了便利店,快遞小哥只需要將同一個小區的快遞放在同一個便利店,然后通知收貨人來取貨就可以了,這時候快遞小哥和收貨人就實現了解耦!
2、 異步
快遞小哥打電話給我后需要一直在你樓下等着,直到我拿走你的快遞他才能去送其他人的。快遞小哥將快遞放在小芳便利店后,又可以干其他的活兒去了,不需要等待你到來而一直處於等待狀態。提高了工作的效率。
3、 削峰
假設雙十一我買了不同店里的各種商品,而恰巧這些店發貨的快遞都不一樣,有中通、圓通、申通、各種通等……更巧的是他們都同時到貨了!中通的小哥打來電話叫我去北門取快遞、圓通小哥叫我去南門、申通小哥叫我去東門。我一時手忙腳亂……
我們能看到在系統需要交互的場景中,使用消息隊列中間件真的是好處多多,基於這種思路,就有了豐巢、菜鳥驛站等比便利店更專業的“中間件”了。
二、消息隊列發展歷程
實際上消息中間件的發展也是挺有意思的,我們知道任何一個技術的出現都是為了解決實際問題,這個 問題是 通過一種通用的軟件總線也就是一種通信系統,解決應用程序之間繁重的信息通信工作。
最早的小白鼠就是金融交易領域,因為在當時這個領域中,交易員需要通過不同的終端完成交易,每台終端顯示不同的信息。
如果接入消息總線,那么交易員只需要在一台終端上操作,然后訂閱其他終端感興趣 的消息。於是就誕生了發布訂閱模型(pubsub),同時誕生了世界上第一個現代消息隊列軟件(TIB) The information Bus, TIB允許開發者建立一系列規則去描述消息內容,只要消息按照這些規則發布出 去,任何消費者應用都能訂閱感興趣的消息。
隨着TIB帶來的甜頭被廣泛應用在各大領域,IBM也開始研 究開發自己的消息中間件,3年后IBM的消息隊列IBM MQ產品系列發布,之后的一段時間MQ系列進化 成了WebSphere MQ統治商業消息隊列平台市場。
包括后期微軟也研發了自己的消息隊列(MSMQ)
各大廠商紛紛研究自己的MQ,但是他們是以商業化模式運營自己的MQ軟件,商業MQ想要解決的是應用互通的問題,而不是創建標准接口來允許不同MQ產品互通。
三、消息隊列通信的模式
通過上面的例子我們引出了消息中間件,並且介紹了消息隊列出現后的好處,這里就需要介紹消息隊列通信的兩種模式了:
1.點對點模式

如上圖所示,點對點模式通常是基於拉取或者輪詢的消息傳送模型,這個模型的特點是發送到隊列的消息被一個且只有一個消費者進行處理。生產者將消息放入消息隊列后,由消費者主動的去拉取消息進行消費。點對點模型的的優點是消費者拉取消息的頻率可以由自己控制。但是消息隊列是否有消息需要消費,在消費者端無法感知,所以在消費者端需要額外的線程去監控。
2. 發布訂閱模式

如上圖所示,發布訂閱模式是一個基於消息送的消息傳送模型,改模型可以有多種不同的訂閱者。生產者將消息放入消息隊列后,隊列會將消息推送給訂閱過該類消息的消費者(類似微信公眾號)。由於是消費者被動接收推送,所以無需感知消息隊列是否有待消費的消息!但是consumer1、consumer2、consumer3由於機器性能不一樣,所以處理消息的能力也會不一樣,但消息隊列卻無法感知消費者消費的速度!所以推送的速度成了發布訂閱模模式的一個問題!假設三個消費者處理速度分別是8M/s、5M/s、2M/s,如果隊列推送的速度為5M/s,則consumer3無法承受!如果隊列推送的速度為2M/s,則consumer1、consumer2會出現資源的極大浪費!
四、消息隊列問題
經過我們上面的場景,我們已經可以發現,消息隊列能做的事其實還是蠻多的。下面我們來看看要實現消息隊列(中間件)可能要考慮什么問題。
1.高可用
無論是我們使用消息隊列來做解耦、異步還是削峰,消息隊列肯定不能是單機的。試着想一下,如果是單機的消息隊列,萬一這台機器掛了,那我們整個系統幾乎就是不可用了。
所以,當我們項目中使用消息隊列,都是得`集群/分布式`的。要做`集群/分布式`就必然希望該消息隊列能夠提供現成的支持,而不是自己寫代碼手動去實現。

2 .數據丟失問題
我們將數據寫到消息隊列上,系統B和C還沒來得及取消息隊列的數據,就掛掉了。如果沒有做任何的措施,我們的數據就丟了。
學過Redis的都知道,Redis可以將數據持久化磁盤上,萬一Redis掛了,還能從磁盤從將數據恢復過來。同樣地,消息隊列中的數據也需要存在別的地方,這樣才盡可能減少數據的丟失。
那存在哪呢?
- 磁盤?
- 數據庫?
- Redis?
- 分布式文件系統?
同步存儲還是異步存儲?

3.消費者怎么得到數據
消費者怎么從消息隊列里邊得到數據?有兩種辦法:
- 生產者將數據放到消息隊列中,消息隊列有數據了,主動叫消費者去拿(俗稱push)
- 消費者不斷去輪詢消息隊列,看看有沒有新的數據,如果有就消費(俗稱pull)
4.其他
除了這些,我們在使用的時候還得考慮各種的問題:
- 消息重復消費了怎么辦啊?
- 我想保證消息是絕對有順序的怎么做?
- ...
雖然消息隊列給我們帶來了那么多的好處,但同時我們發現引入消息隊列也會提高系統的復雜性。市面上現在已經有不少消息隊列輪子了,每種消息隊列都有自己的特點,選取哪種MQ還得好好斟酌。
五、主流消息隊列中間件比較
目前在市面上比較主流的消息隊列中間件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ 等這幾種。
ActiveMQ和RabbitMQ這兩着因為吞吐量還有GitHub的社區活躍度的原因,在各大互聯網公司都已經基本上絕跡了,業務體量一般的公司會是有在用的,但是越來越多的公司更青睞RocketMQ這樣的消息中間件了。
Kafka和RocketMQ一直在各自擅長的領域發光發亮。

上圖很明顯就能看到差距了:
就拿吞吐量來說,早期比較活躍的ActiveMQ 和RabbitMQ基本上不是后兩者的對手了,在現在這樣大數據的年代吞吐量是真的很重要。比如現在突然爆發了一個超級熱點新聞,你的APP注冊用戶高達億數,你要想辦法第一時間把突發全部推送到每個人手上,你沒有大吞吐量的消息隊列中間件用啥去推?再說這些用戶大量涌進來看了你的新聞產生了一系列的附帶流量,你怎么應對這些數據,很多場景離開消息隊列基本上難以為繼。就部署方式而言前兩者也是大不如后面兩個天然分布式架構的哥哥,都是高可用的分布式架構,而且數據多個副本的數據也能做到0丟失。
我們再聊一下RabbitMQ這個中間件其實還行,但是這玩意開發語言居然是erlang,我敢說絕大部分工程師肯定不會為了一個中間件去刻意學習一門語言的,開發維護成本你想都想不到,出個問題查都查半天。至於RocketMQ(阿里開源的),git活躍度還可以。基本上你push了自己的bug確認了有問題都有阿里大佬跟你試試解答並修復的,他的架構設計部分跟同樣是阿里開源的一個RPC框架是真的很像(Dubbo)可能是因為師出同門的原因吧。
Kafka我放到最后說,你們也應該知道了,壓軸的這是個大哥,大數據領域,公司的日志采集,實時計算等場景,都離不開他的身影,他基本上算得上是世界范圍級別的消息隊列標桿了。以上這些都只是一些我自己的個人意見,真正的選型還是要去深入研究的,不然那你公司一天UV就1000你告訴我你要去用Kafka我只能說你吃飽撐的。記住,沒有最好的技術,只有最適合的技術,不要為了用而用。