新手也能看懂,消息隊列其實很簡單

該文已加入開源項目:JavaGuide(一份涵蓋大部分Java程序員所須要掌握的核心知識的文檔類項目,Star 數接近 16k)。地址:github.com/Snailclimb/….java

本文內容思惟導圖: git

消息隊列總結

消息隊列其實很簡單

  「RabbitMQ?」「Kafka?」「RocketMQ?」...在平常學習與開發過程當中,咱們經常聽到消息隊列這個關鍵詞。我也在個人多篇文章中提到了這個概念。可能你是熟練使用消息隊列的老手,又或者你是不懂消息隊列的新手,不論你了不瞭解消息隊列,本文都將帶你搞懂消息隊列的一些基本理論。若是你是老手,你可能從本文學到你以前未曾注意的一些關於消息隊列的重要概念,若是你是新手,相信本文將是你打開消息隊列大門的一板磚。程序員

一 什麼是消息隊列

  咱們能夠把消息隊列比做是一個存放消息的容器,當咱們須要使用消息的時候能夠取出消息供本身使用。消息隊列是分佈式系統中重要的組件,使用消息隊列主要是爲了經過異步處理提升系統性能和削峯、下降系統耦合性。目前使用較多的消息隊列有ActiveMQ,RabbitMQ,Kafka,RocketMQ,咱們後面會一一對比這些消息隊列。github

  另外,咱們知道隊列 Queue 是一種先進先出的數據結構,因此消費消息時也是按照順序來消費的。好比生產者發送消息1,2,3...對於消費者就會按照1,2,3...的順序來消費。可是偶爾也會出現消息被消費的順序不對的狀況,好比某個消息消費失敗又或者一個 queue 多個consumer 也會致使消息被消費的順序不對,咱們必定要保證消息被消費的順序正確。面試

  除了上面說的消息消費順序的問題,使用消息隊列,咱們還要考慮如何保證消息不被重複消費?如何保證消息的可靠性傳輸(如何處理消息丟失的問題)?......等等問題。因此說使用消息隊列也不是十全十美的,使用它也會讓系統可用性下降、複雜度提升,另外須要咱們保障一致性等問題。算法

二 爲何要用消息隊列

  我以爲使用消息隊列主要有兩點好處:1.經過異步處理提升系統性能(削峯、減小響應所需時間);2.下降系統耦合性。若是在面試的時候你被面試官問到這個問題的話,通常狀況是你在你的簡歷上涉及到消息隊列這方面的內容,這個時候推薦你結合你本身的項目來回答。數據庫

  《大型網站技術架構》第四章和第七章均有提到消息隊列對應用性能及擴展性的提高。服務器

(1) 經過異步處理提升系統性能(削峯、減小響應所需時間)

經過異步處理提升系統性能
  如上圖, 在不使用消息隊列服務器的時候,用戶的請求數據直接寫入數據庫,在高併發的狀況下數據庫壓力劇增,使得響應速度變慢。可是在使用消息隊列以後,用戶的請求數據發送給消息隊列以後當即 返回,再由消息隊列的消費者進程從消息隊列中獲取數據,異步寫入數據庫。因爲消息隊列服務器處理速度快於數據庫(消息隊列也比數據庫有更好的伸縮性),所以響應速度獲得大幅改善。

  經過以上分析咱們能夠得出消息隊列具備很好的削峯做用的功能——即經過異步處理,將短期高併發產生的事務消息存儲在消息隊列中,從而削平高峯期的併發事務。 舉例:在電子商務一些秒殺、促銷活動中,合理使用消息隊列能夠有效抵禦促銷活動剛開始大量訂單涌入對系統的衝擊。以下圖所示: 數據結構

合理使用消息隊列能夠有效抵禦促銷活動剛開始大量訂單涌入對系統的衝擊

  由於用戶請求數據寫入消息隊列以後就當即返回給用戶了,可是請求數據在後續的業務校驗、寫數據庫等操做中可能失敗。所以使用消息隊列進行異步處理以後,須要適當修改業務流程進行配合,好比用戶在提交訂單以後,訂單數據寫入消息隊列,不能當即返回用戶訂單提交成功,須要在消息隊列的訂單消費者進程真正處理完該訂單以後,甚至出庫後,再經過電子郵件或短信通知用戶訂單成功,以避免交易糾紛。這就相似咱們平時手機訂火車票和電影票。架構

(2) 下降系統耦合性

  咱們知道若是模塊之間不存在直接調用,那麼新增模塊或者修改模塊就對其餘模塊影響較小,這樣系統的可擴展性無疑更好一些。

  咱們最多見的事件驅動架構相似生產者消費者模式,在大型網站中一般用利用消息隊列實現事件驅動結構。以下圖所示:

利用消息隊列實現事件驅動結構

  消息隊列使利用發佈-訂閱模式工做,消息發送者(生產者)發佈消息,一個或多個消息接受者(消費者)訂閱消息。 從上圖能夠看到消息發送者(生產者)和消息接受者(消費者)之間沒有直接耦合,消息發送者將消息發送至分佈式消息隊列即結束對消息的處理,消息接受者從分佈式消息隊列獲取該消息後進行後續處理,並不須要知道該消息從何而來。對新增業務,只要對該類消息感興趣,便可訂閱該消息,對原有系統和業務沒有任何影響,從而實現網站業務的可擴展性設計

  消息接受者對消息進行過濾、處理、包裝後,構形成一個新的消息類型,將消息繼續發送出去,等待其餘消息接受者訂閱該消息。所以基於事件(消息對象)驅動的業務架構能夠是一系列流程。

  另外爲了不消息隊列服務器宕機形成消息丟失,會將成功發送到消息隊列的消息存儲在消息生產者服務器上,等消息真正被消費者服務器處理後才刪除消息。在消息隊列服務器宕機後,生產者服務器會選擇分佈式消息隊列服務器集羣中的其餘服務器發佈消息。

備註: 不要認爲消息隊列只能利用發佈-訂閱模式工做,只不過在解耦這個特定業務環境下是使用發佈-訂閱模式的。除了發佈-訂閱模式,還有點對點訂閱模式(一個消息只有一個消費者),咱們比較經常使用的是發佈-訂閱模式。 另外,這兩種消息模型是 JMS 提供的,AMQP 協議還提供了 5 種消息模型。

三 使用消息隊列帶來的一些問題

  • 系統可用性下降: 系統可用性在某種程度上下降,爲何這樣說呢?在加入MQ以前,你不用考慮消息丟失或者說MQ掛掉等等的狀況,可是,引入MQ以後你就須要去考慮了!
  • 系統複雜性提升: 加入MQ以後,你須要保證消息沒有被重複消費、處理消息丟失的狀況、保證消息傳遞的順序性等等問題!
  • 一致性問題: 我上面講了消息隊列能夠實現異步,消息隊列帶來的異步確實能夠提升系統響應速度。可是,萬一消息的真正消費者並無正確消費消息怎麼辦?這樣就會致使數據不一致的狀況了!

四 JMS VS AMQP

4.1 JMS

4.1.1 JMS 簡介

  JMS(JAVA Message Service,java消息服務)是java的消息服務,JMS的客戶端之間能夠經過JMS服務進行異步的消息傳輸。JMS(JAVA Message Service,Java消息服務)API是一個消息服務的標準或者說是規範,容許應用程序組件基於JavaEE平臺建立、發送、接收和讀取消息。它使分佈式通訊耦合度更低,消息服務更加可靠以及異步性。

ActiveMQ 就是基於 JMS 規範實現的。

4.1.2 JMS兩種消息模型

①點到點(P2P)模型

點到點(P2P)模型
  使用 隊列(Queue)做爲消息通訊載體;知足生產者與消費者模式,一條消息只能被一個消費者使用,未被消費的消息在隊列中保留直到被消費或超時。好比:咱們生產者發送100條消息的話,兩個消費者來消費通常狀況下兩個消費者會按照消息發送的順序各自消費一半(也就是你一個我一個的消費。)

② 發佈/訂閱(Pub/Sub)模型

發佈/訂閱(Pub/Sub)模型
  發佈訂閱模型(Pub/Sub) 使用 主題(Topic)做爲消息通訊載體,相似於廣播模式;發佈者發佈一條消息,該消息經過主題傳遞給全部的訂閱者, 在一條消息廣播以後才訂閱的用戶則是收不到該條消息的

4.1.3 JMS 五種不一樣的消息正文格式

  JMS定義了五種不一樣的消息正文格式,以及調用的消息類型,容許你發送並接收以一些不一樣形式的數據,提供現有消息格式的一些級別的兼容性。

  • StreamMessage -- Java原始值的數據流
  • MapMessage--一套名稱-值對
  • TextMessage--一個字符串對象
  • ObjectMessage--一個序列化的 Java對象
  • BytesMessage--一個字節的數據流

4.2 AMQP

  ​ AMQP,即Advanced Message Queuing Protocol,一個提供統一消息服務的應用層標準 高級消息隊列協議(二進制應用層協議),是應用層協議的一個開放標準,爲面向消息的中間件設計,兼容 JMS。基於此協議的客戶端與消息中間件可傳遞消息,並不受客戶端/中間件同產品,不一樣的開發語言等條件的限制。

RabbitMQ 就是基於 AMQP 協議實現的。

4.3 JMS vs AMQP

對比方向 JMS AMQP
定義 Java API 協議
跨語言
跨平臺
支持消息類型 提供兩種消息模型:①Peer-2-Peer;②Pub/sub 提供了五種消息模型:①direct exchange;②fanout exchange;③topic change;④headers exchange;⑤system exchange。本質來說,後四種和JMS的pub/sub模型沒有太大差異,僅是在路由機制上作了更詳細的劃分;
支持消息類型 支持多種消息類型 ,咱們在上面提到過 byte[](二進制)

總結:

  • AMQP 爲消息定義了線路層(wire-level protocol)的協議,而JMS所定義的是API規範。在 Java 體系中,多個client都可以經過JMS進行交互,不須要應用修改代碼,可是其對跨平臺的支持較差。而AMQP自然具備跨平臺、跨語言特性。
  • JMS 支持TextMessage、MapMessage 等複雜的消息類型;而 AMQP 僅支持 byte[] 消息類型(複雜的類型可序列化後發送)。
  • 因爲Exchange 提供的路由算法,AMQP能夠提供多樣化的路由方式來傳遞消息到消息隊列,而 JMS 僅支持 隊列 和 主題/訂閱 方式兩種。

五 常見的消息隊列對比

對比方向 概要
吞吐量 萬級的 ActiveMQ 和 RabbitMQ 的吞吐量(ActiveMQ 的性能最差)要比 十萬級甚至是百萬級的 RocketMQ 和 Kafka 低一個數量級。
可用性 均可以實現高可用。ActiveMQ 和 RabbitMQ 都是基於主從架構實現高可用性。RocketMQ 基於分佈式架構。 kafka 也是分佈式的,一個數據多個副本,少數機器宕機,不會丟失數據,不會致使不可用
時效性 RabbitMQ 基於erlang開發,因此併發能力很強,性能極其好,延時很低,達到微秒級。其餘三個都是 ms 級。
功能支持 除了 Kafka,其餘三個功能都較爲完備。 Kafka 功能較爲簡單,主要支持簡單的MQ功能,在大數據領域的實時計算以及日誌採集被大規模使用,是事實上的標準
消息丟失 ActiveMQ 和 RabbitMQ 丟失的可能性很是低, RocketMQ 和 Kafka 理論上不會丟失。

總結:

  • ActiveMQ 的社區算是比較成熟,可是較目前來講,ActiveMQ 的性能比較差,並且版本迭代很慢,不推薦使用。
  • RabbitMQ 在吞吐量方面雖然稍遜於 Kafka 和 RocketMQ ,可是因爲它基於 erlang 開發,因此併發能力很強,性能極其好,延時很低,達到微秒級。可是也由於 RabbitMQ 基於 erlang 開發,因此國內不多有公司有實力作erlang源碼級別的研究和定製。若是業務場景對併發量要求不是過高(十萬級、百萬級),那這四種消息隊列中,RabbitMQ 必定是你的首選。若是是大數據領域的實時計算、日誌採集等場景,用 Kafka 是業內標準的,絕對沒問題,社區活躍度很高,絕對不會黃,況且幾乎是全世界這個領域的事實性規範。
  • RocketMQ 阿里出品,Java 系開源項目,源代碼咱們能夠直接閱讀,而後能夠定製本身公司的MQ,而且 RocketMQ 有阿里巴巴的實際業務場景的實戰考驗。RocketMQ 社區活躍度相對較爲通常,不過也還能夠,文檔相對來講簡單一些,而後接口這塊不是按照標準 JMS 規範走的有些系統要遷移須要修改大量代碼。還有就是阿里出臺的技術,你得作好這個技術萬一被拋棄,社區黃掉的風險,那若是大家公司有技術實力我以爲用RocketMQ 挺好的
  • kafka 的特色其實很明顯,就是僅僅提供較少的核心功能,可是提供超高的吞吐量,ms 級的延遲,極高的可用性以及可靠性,並且分佈式能夠任意擴展。同時 kafka 最好是支撐較少的 topic 數量便可,保證其超高吞吐量。kafka 惟一的一點劣勢是有可能消息重複消費,那麼對數據準確性會形成極其輕微的影響,在大數據領域中以及日誌採集中,這點輕微影響能夠忽略這個特性自然適合大數據實時計算以及日誌收集。

參考:《Java工程師面試突擊第1季-中華石杉老師》

ThoughtWorks准入職Java工程師。專一Java知識分享!開源 Java 學習指南——JavaGuide(15k+ Star)的做者。公衆號多篇文章被各大技術社區轉載。公衆號後臺回覆關鍵字「1」能夠領取一份我精選的Java資源哦!

個人公衆號
相關文章
相關標籤/搜索