各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

RabbitMQ簡介

RabbitMQ是什麼定義

RabbitMQ是一個開源的AMQP實現,服務器端用Erlang語言編寫,支持多種客戶端,如:Python、Ruby、.NET、Java、JMS、C、PHP、Action、XMPP、STOMP等,支持AJAX。用於在分佈式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。

AMPQ

AMQP,即Advanced Message Queuing Protocol,高級消息隊列協議,是應用層協議的一個開放標準,為面向消息的中間件設計。消息中間件主要用於組件之間的解耦,消息的發送者無需知道消息使用者的存在,反之亦然。

它可以使對應的客戶端(client)與對應的消息中間件(broker)進行交互。消息中間件從發佈者(publisher)那裡收到消息(發佈消息的應用,也稱為producer),然後將他們轉發給消費者(consumers,處理消息的應用)。由於AMQP是一個網絡協議,所以發佈者、消費者以及消息中間件可以部署到不同的物理機器上面。

雖然在同步消息通訊的世界裡有很多公開標準(如 COBAR的 IIOP ,或者是 SOAP 等),但是在異步消息處理中卻不是這樣,只有大企業有一些商業實現(如微軟的 MSMQ ,IBM 的 Websphere MQ 等),因此,在 2006 年的 6 月,Cisco 、Redhat、iMatix 等聯合制定了 AMQP 的公開標準。

RabbitMQ是由RabbitMQ Technologies Ltd開發並且提供商業支持的。該公司在2010年4月被SpringSource(VMWare的一個部門)收購。在2013年5月被併入Pivotal。其實VMWare,Pivotal和EMC本質上是一家的。不同的是VMWare是獨立上市子公司,而Pivotal是整合了EMC的某些資源,現在並沒有上市。

ConnectionFactory、Connection、Channel

ConnectionFactory、Connection、Channel都是RabbitMQ對外提供的API中最基本的對象。Connection是RabbitMQ的socket鏈接,它封裝了socket協議相關部分邏輯。ConnectionFactory為Connection的製造工廠。 Channel是我們與RabbitMQ打交道的最重要的一個接口,我們大部分的業務操作是在Channel這個接口中完成的,包括定義Queue、定義Exchange、綁定Queue與Exchange、發佈消息等。

Queue

Queue(隊列)是RabbitMQ的內部對象,用於存儲消息,用下圖表示。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

RabbitMQ中的消息都只能存儲在Queue中,生產者(下圖中的P)生產消息並最終投遞到Queue中,消費者(下圖中的C)可以從Queue中獲取消息並消費。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

多個消費者可以訂閱同一個Queue,這時Queue中的消息會被平均分攤給多個消費者進行處理,而不是每個消費者都收到所有的消息並處理。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

Exchange

在上一節我們看到生產者將消息投遞到Queue中,實際上這在RabbitMQ中這種事情永遠都不會發生。實際的情況是,生產者將消息發送到Exchange(交換器,下圖中的X),由Exchange將消息路由到一個或多個Queue中(或者丟棄)。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

Exchange是按照什麼邏輯將消息路由到Queue的?這個將在Binding一節介紹。 RabbitMQ中的Exchange有四種類型,不同的類型有著不同的路由策略,這將在Exchange Types一節介紹。

routing key

生產者在將消息發送給Exchange的時候,一般會指定一個routing key,來指定這個消息的路由規則,而這個routing key需要與Exchange Type及binding key聯合使用才能最終生效。 在Exchange Type與binding key固定的情況下(在正常使用時一般這些內容都是固定配置好的),我們的生產者就可以在發送消息給Exchange時,通過指定routing key來決定消息流向哪裡。 RabbitMQ為routing key設定的長度限制為255 bytes。

Binding

RabbitMQ中通過Binding將Exchange與Queue關聯起來,這樣RabbitMQ就知道如何正確地將消息路由到指定的Queue了。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

Binding key

在綁定(Binding)Exchange與Queue的同時,一般會指定一個binding key;消費者將消息發送給Exchange時,一般會指定一個routing key;當binding key與routing key相匹配時,消息將會被路由到對應的Queue中。這個將在Exchange Types章節會列舉實際的例子加以說明。 在綁定多個Queue到同一個Exchange的時候,這些Binding允許使用相同的binding key。 binding key 並不是在所有情況下都生效,它依賴於Exchange Type,比如fanout類型的Exchange就會無視binding key,而是將消息路由到所有綁定到該Exchange的Queue。

Exchange Types

RabbitMQ常用的Exchange Type有fanout、direct、topic、headers這四種(AMQP規範裡還提到兩種Exchange Type,分別為system與自定義,這裡不予以描述),下面分別進行介紹。

fanout

fanout類型的Exchange路由規則非常簡單,它會把所有發送到該Exchange的消息路由到所有與它綁定的Queue中。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

上圖中,生產者(P)發送到Exchange(X)的所有消息都會路由到圖中的兩個Queue,並最終被兩個消費者(C1與C2)消費。

direct

direct類型的Exchange路由規則也很簡單,它會把消息路由到那些binding key與routing key完全匹配的Queue中。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

以上圖的配置為例,我們以routingKey=”error”發送消息到Exchange,則消息會路由到Queue1(amqp.gen-S9b…,這是由RabbitMQ自動生成的Queue名稱)和Queue2(amqp.gen-Agl…);如果我們以routingKey=”info”或routingKey=”warning”來發送消息,則消息只會路由到Queue2。如果我們以其他routingKey發送消息,則消息不會路由到這兩個Queue中。

topic

前面講到direct類型的Exchange路由規則是完全匹配binding key與routing key,但這種嚴格的匹配方式在很多情況下不能滿足實際業務需求。topic類型的Exchange在匹配規則上進行了擴展,它與direct類型的Exchage相似,也是將消息路由到binding key與routing key相匹配的Queue中,但這裡的匹配規則有些不同,它約定:

  • routing key為一個句點號“. ”分隔的字符串(我們將被句點號“. ”分隔開的每一段獨立的字符串稱為一個單詞),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
  • binding key與routing key一樣也是句點號“. ”分隔的字符串
  • binding key中可以存在兩種特殊字符“*”與“#”,用於做模糊匹配,其中“*”用於匹配一個單詞,“#”用於匹配多個單詞(可以是零個)
各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

以上圖中的配置為例,routingKey=”quick.orange.rabbit”的消息會同時路由到Q1與Q2,routingKey=”lazy.orange.fox”的消息會路由到Q1與Q2,routingKey=”lazy.brown.fox”的消息會路由到Q2,routingKey=”lazy.pink.rabbit”的消息會路由到Q2(只會投遞給Q2一次,雖然這個routingKey與Q2的兩個bindingKey都匹配);routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的消息將會被丟棄,因為它們沒有匹配任何bindingKey。

headers

headers類型的Exchange不依賴於routing key與binding key的匹配規則來路由消息,而是根據發送的消息內容中的headers屬性進行匹配。 在綁定Queue與Exchange時指定一組鍵值對;當消息發送到Exchange時,RabbitMQ會取到該消息的headers(也是一個鍵值對的形式),對比其中的鍵值對是否完全匹配Queue與Exchange綁定時指定的鍵值對;如果完全匹配則消息會路由到該Queue,否則不會路由到該Queue。 該類型的Exchange沒有用到過(不過也應該很有用武之地),所以不做介紹。

RPC

MQ本身是基於異步的消息處理,前面的示例中所有的生產者(P)將消息發送到RabbitMQ後不會知道消費者(C)處理成功或者失敗(甚至連有沒有消費者來處理這條消息都不知道)。 但實際的應用場景中,我們很可能需要一些同步處理,需要同步等待服務端將我的消息處理完成後再進行下一步處理。這相當於RPC(Remote Procedure Call,遠程過程調用)。在RabbitMQ中也支持RPC。

各種MQ的對比之後,我們依然還是選擇了 RabbitMQ,為什麼呢?

RabbitMQ 中實現RPC 的機制是:

  • 客戶端發送請求(消息)時,在消息的屬性(MessageProperties ,在AMQP 協議中定義了14中properties ,這些屬性會隨著消息一起發送)中設置兩個值replyTo (一個Queue 名稱,用於告訴服務器處理完成後將通知我的消息發送到這個Queue 中)和correlationId (此次請求的標識號,服務器處理完成後需要將此屬性返還,客戶端將根據這個id瞭解哪條請求被成功執行了或執行失敗)
  • 服務器端收到消息並處理
  • 服務器端處理完消息後,將生成一條應答消息到replyTo 指定的Queue ,同時帶上correlationId 屬性
  • 客戶端之前已訂閱replyTo 指定的Queue ,從中收到服務器的應答消息後,根據其中的correlationId屬性分析哪條請求被執行了,根據執行結果進行後續業務處理

其它同類產品

消息中間件是一種由消息傳送機制或消息隊列模式組成的中間件技術,利用高效可靠的消息傳遞機制進行平臺無關的數據交流,並基於數據通信來進行分佈式系統的集成。

Redis

Redis是一個Key-Value的NoSQL數據庫,開發維護很活躍,雖然它是一個Key-Value數據庫存儲系統,但它本身支持MQ功能,所以完成可以當做一個輕量級的隊列服務來使用。對於RabbitMQ和Redis的入隊和出隊操作,各執行100萬次,每10萬次記錄一次執行時間。測試數據分為128Bytes、512Bytes、1K和10K四個不同大小的數據。實驗表明:入隊時,當數據比較小時,Redis的性能要高於RabbitMQ,而如否數據大小超過了10K,Redis則慢的無法忍受;出隊時,無論數據大小,Redis都表現出非常好的性能,而RabbitMQ的出隊性能則遠低於Redis。

MemcacheQ

持久化消息隊列(簡稱mcq)是一個輕量級的消息隊列,特性如下:

  • 簡單易用
  • 處理速度快
  • 多條隊列
  • 併發性能好
  • 與memcache的協議兼容。意味著只要裝了前者的extension即可,不需要額外的插件
  • 在zend framework中使用很方便

MSMQ

這是微軟的產品力唯一被認為有價值的東西。如果MSMQ能證明可以應對這種任務,他們將選擇使用它。

  • 關鍵是它並不複雜,除了接收和發送,沒有別的;它有一些硬性限制,比如最大消息體積是4MB。
  • 然而,通過和一些想MassTransit或NServiceBus這樣的軟件的連接,它完全可以解決這些問題。

ZeroMQ

ZeroMQ是一個非常輕量級的消息系統,號稱最快的消息隊列系統,專門為高吞吐量/低延遲的場景開發,在金融界的應用中經常可以發現它。

  • 與RabbitMQ相比,ZeroMQ支持許多高級消息場景,能夠實現RabbitMQ不擅長的高級/複雜的隊列,但是你必須實現ZeroMQ框架中的各個塊(比如Socket或Device等)。
  • ZeroMQ具有一個獨特的非中間件的模式,你不需要安裝和運行一個消息服務器或中間件,因為你的應用程序將扮演這個服務角色。你只需要簡單地引用ZeroMQ程序庫,可以使用NuGet安裝,然後你就可以愉快地在應用程序之間發送消息了。
  • 但是ZeroMQ僅提供非持久性的隊列,即沒有地方可以觀察它是否有問題出現,也就是說如果down機,數據將會丟失。
  • ZeroMQ非常靈活,但是你必須學習它的80頁的手冊(如果你要寫一個分佈式系統,一定要閱讀它)。

Jafka/Kafka

Kafka(能將消息分散到不同的節點上)是LinkedIn於2010年12月開發並開源的一個分佈式MQ系統,現在是Apache的一個孵化項目,是一個高性能跨語言分佈式Publish/Subscribe消息隊列系統,而Jafka是在Kafka之上孵化而來的,即Kafka的一個升級版。具有以下特性:

  • 快速持久化,可以在O(1)的系統開銷下進行消息持久化;
  • 高吞吐,在一臺普通的服務器上既可以打到10W/s的吞吐速率;
  • 完全的分佈式系統,Broker、Producer、Consumer都原生自動支持分佈式,自動實現複雜均衡;
  • 支持Hadoop數據並行加載,統一了在線和離線的消息處理,對於像Hadoop一樣的日誌數據和離線分析系統,但又要求實時處理的限制,這是一個可行的解決方案。
  • 相對於ActiveMQ是一個非常輕量級的消息系統,除了性能非常好之外,還是一個工作良好的分佈式系統。

Apache ActiveMQ

ActiveMQ居於(RabbitMQ&ZeroMQ)之間,類似於ZemoMQ,它可以部署於代理模式和P2P模式。

  • ActiveMQ被譽為Java世界的中堅力量。它有很長的歷史,且被廣泛使用。它還是跨平臺的,給那些非微軟平臺的產品提供了一個天然的集成接入點。
  • 然而它只有跑過了MSMQ才有可能被考慮。如需配置ActiveMQ則需要在目標機器上安裝Java環境。
  • 類似於RabbitMQ,它易於實現高級場景,而且只需付出低消耗。它被譽為消息中間件的“瑞士軍刀”。

RabbitMQ

RabbitMQ是使用Erlang編寫的一個開源消息隊列,本身支持很多的協議:AMQP, XMPP, SMTP, STONP,也正是如此,使的它變的非常重量級,更適合於企業級的開發。

  • 它實現了代理(Broker)架構,意味著消息在發送到客戶端之前可以在中央節點上排隊。此特性使得RabbitMQ易於使用和部署,適宜於很多場景如路由、負載均衡或消息持久化等,用消息隊列只需幾行代碼即可搞定。
  • 但是,這使得它的可擴展性差,速度較慢,因為中央節點增加了延遲,消息封裝後也比較大。
  • 如需配置RabbitMQ則需要在目標機器上安裝Erlang環境。

對比總結

1.從社區活躍度

按照目前網絡上的資料,RabbitMQ 、activeM 、ZeroMQ 三者中,綜合來看,RabbitMQ 是首選。

2.持久化消息比較

ZeroMq 不支持,ActiveMq 和RabbitMq 都支持。持久化消息主要是指我們機器在不可抗力因素等情況下掛掉了,消息不會丟失的機制。

3.綜合技術實現

可靠性、靈活的路由、集群、事務、高可用的隊列、消息排序、問題追蹤、可視化管理工具、插件系統等等。

RabbitMq / Kafka 最好,ActiveMq 次之,ZeroMq 最差。當然ZeroMq 也可以做到,不過自己必須手動寫代碼實現,代碼量不小。尤其是可靠性中的:持久性、投遞確認、發佈者證實和高可用性。

4.高併發

毋庸置疑,RabbitMQ 最高,原因是它的實現語言是天生具備高併發高可用的erlang 語言。

5.比較關注的比較, RabbitMQ 和 Kafka

RabbitMq 比Kafka 成熟,在可用性上,穩定性上,可靠性上, RabbitMq 勝於 Kafka (理論上)。

另外,Kafka 的定位主要在日誌等方面, 因為Kafka 設計的初衷就是處理日誌的,可以看做是一個日誌(消息)系統一個重要組件,針對性很強,所以 如果業務方面還是建議選擇 RabbitMq 。

還有就是,Kafka 的性能(吞吐量、TPS )比RabbitMq 要高出來很多。

(此處已添加圈子卡片,請到今日頭條客戶端查看)


分享到:


相關文章: