常用高可靠消息隊列(MQ)系統選型

MQ可靠消息傳遞背景知識

消息隊列是大多數分佈式系統的核心,給服務/微服務的異步處理提供支持。

  • 當異步執行任務時,可以將任務寫入隊列,其他組件(線程、進程)讀取到該任務後運行。
  • 消息送達處理任務隊列時,我們希望它能夠被快速讀取,異步處理。
  • 在提高可靠消息傳遞前提下(我們希望確保消息發送完成後,即使節點或系統崩潰,該消息也將永久排隊並最終將被傳遞)測試如下幾種常見MQ系統:

  • Mongo DB
  • RabbitMq
  • ActiveMQ
  • Kafka
  • 系統通過以下方式確保消息高可靠性:

  • 將消息持久保存到磁盤
  • 通過網絡複製消息
  • MQ滿足條件

    1. 作為發送方,我們希望一旦消息發送成功,消息就會得到處理。當然,不可能達到100%成功,將消息丟失可能性降到最低:消息在服務器重啟不丟失,消息應保留在持久性硬盤中;服務器故障消息不丟失,消息應複製到其他服務器後才發送。
    2. 作為接收方,接收一條消息,確認該消息已成功處理。消息接收不應立刻將消息從隊列中刪除,因為接收方可能隨時崩潰(包括接收後,處理之前)。可能導致消息被處理兩次(例如,接收方在處理之後 ,確認之前崩潰)。
    3. 確認一條消息成功處理。對於同一次消息傳遞,消息處理應該是冪等的,即兩次處理消息應該不會造成任何問題(業務保證重新發送了某個消息,不會造成任何損害)。

    測試及結果

    測試維度:

    • 消息吞吐量/秒:隊列平均速度,即每秒可以發送多少條消息,每秒可以接收和確認多少條消息
    • 處理延遲95th percentile (1分鐘窗口內):消息發送和消息接收之間經過的時間(以毫秒為單位)。也就是說,代理將消息從發送方傳遞到接收方的速度有多快
    • 發送延遲95th percentile (1分鐘窗口內):完成一條消息發送需要多長時間,才確保消息安全地保留在群集中。類似響應客戶的http請求“已收到郵件”。(95th percentile=X,表示95%的數值都比X低或高)

    測試過程:

    測試隊列部署在3個相同集群節點上,集群自動處理故障。測試使用了1到8個客戶端節點(客戶端和接收方節點的數量始終相同),每個節點運行1到25個線程,每個線程嘗試以每一批為1到10條(消息大小隨機)成批發送定量消息(每一批最多100或1000條消息)。接收節點接收消息後,確認消息(隊列刪除該消息)。當一分鐘內未收到任何消息時,測試結束。

    • sender 應該是同步的,也就是說,send完成時,我們要確保消息發送成功。
    • receiver應該從隊列中接收消息,如果receive節點崩潰,則應將消息返回到隊列並重新發送。
    • ACK確認消息的傳遞和處理。ACK可以是異步的,不必確保ACK成功。
    常用高可靠消息隊列(MQ)系統選型

    測試硬件:

    測試服務器才有AWS m4.2xlarge EC2 實例;每個這樣的實例8個虛擬CPU,32GiB的RAM和SSD存儲。所有實例均在同一可用區(eu-west-1)中啟動。

    Mongo

    常用高可靠消息隊列(MQ)系統選型

    Mongo有兩個重要功能,可以輕鬆在其上實現持久化

    隊列之間消息複製:非常簡單的複製設置(使用3節點副本)

    文檔原子性操作:例如find-and-modify,該實現只是幾行代碼(MongoMq項目)。

    • WriteConcern.W1確保發送完成後,消息已被寫入單個節點上的磁盤(但是緩衝區可能尚未刷新,因此不是100%成功),異步複製。
    • WriteConcern.W2確保將消息寫入集群中的至少2個節點(共有3個節點),同步複製。

    Mongo的隊列的主要缺點是:

    • 無法批量接收消息,find-and-modify操作一次只能處理一個文檔。
    • 當有很多連接嘗試接收消息時,並且所有接受操作都將順序執行。

    異步複製,測試結果:

    • 發送比接收快。但是總體表現還是不錯的!
    • 單線程,單節點同步複製可實現1346 msgs / s發送和接收速度。
    • 多線程/節點的最大發送吞吐量約為25038 msgs / s(25個線程,2個節點),最大接收速率為8273 msgs / s(25個線程,1個節點)
    常用高可靠消息隊列(MQ)系統選型

    異步複製,測試結果:

    發送速度高達57555 msg / s,接收速度高達9937 msgs / s。發送和接收性能之間的差異更大。需要注意的一件有趣的事是,接收吞吐量迅速達到了最大值,而增加更多線程(客戶端)只會降低性能。併發性越高,總體吞吐量就越低。

    常用高可靠消息隊列(MQ)系統選型

    延遲測試結果:

    • 發送延遲,在同步和異步測試中,約為48毫秒,並且增加併發客戶端不會惡化。
    • 處理延遲,只有在接收速率與發送速率相同時,測量才有意義。如果客戶端無法像發送時一樣快地接收消息,則處理時間會任意增加。

    在2個節點各自運行5個線程的情況下,Mongo實現了7357 msgs / s的吞吐量和48 ms的處理延遲。該測試結果儀表盤:

    常用高可靠消息隊列(MQ)系統選型

    注意:接收速率有些不均勻-兩個接收器節點都收到不同數量的消息。使用同步複製時的詳細結果:

    常用高可靠消息隊列(MQ)系統選型

    總的來說,基於Mongo 實現隊列MQ,效果還不錯。

    RabbitMQ

    常用高可靠消息隊列(MQ)系統選型

    RabbitMQ是領先的開源消息系統。它用Erlang編寫,實現AMQP協議。使用RabbitMQ,可以定義非常複雜的消息傳遞拓撲。它支持消息持久性和複製。

    我們測試3節點Rabbit集群。為確保發送成功完成,我們將使用發送Confirms,即AMQP的Rabbit擴展,而不是交易。

    測試結果:

    單線程單節點使我們能夠發送和接收680 msgs / s的數據,處理延遲為184 ms,發送延遲為48 ms

    常用高可靠消息隊列(MQ)系統選型

    常用高可靠消息隊列(MQ)系統選型

    當我們增加線程/節點時,使用4個發送/接收節點(每個線程運行25個線程),最高達到到4330 msgs / s,這似乎是Rabbit可以達到的最大值。

    看測試儀表板,接收速率在40005000 msgs / s之間相當穩定。整個測試過程中的處理延遲都基本相同,第95個百分位數為

    179 ms。但是,發送延遲在短時間內上升到504ms。因此,在某些情況下,甚至在發送完成之前,消息就已經被處理了!但是,在大多數情況下,發送延遲在150-250毫秒之間。

    常用高可靠消息隊列(MQ)系統選型

    消息始終以相同的速率發送和接收,這表明消息發送是吞吐量的限制因素。Rabbit的性能不足是它提供的某些功能的結果。

    有趣的是,以更大的批發送消息不會對總體吞吐量產生太大影響,當使用100消息每批時為

    4550msgs/s,當我們批處理1000消息每批次時為5004msgs/s

    重要的是:RabbitMQ具有一個基於Web的出色控制檯,幾乎無需設置即可使用,它提供了有關隊列執行情況的一些很好的見解。

    ActiveMQ

    常用高可靠消息隊列(MQ)系統選型

    ActiveMQ是最受歡迎的消息代理之一。但是,它只是最近才支持複製功能。在版本5.9.0之前,使用共享文件系統(例如SAN)或共享數據庫進行主從設置。這些解決方案需要專門的硬件,或者受關係數據庫的約束。

    但是,現在可以使用Replicated LevelDB存儲,該存儲使用Zookeeper進行集群協調(例如Kafka)。

    複製可以是同步的,也可以是異步的。實際上,它具有很大的靈活性。通過設置sync存儲的配置,我們可以控制完成請求之前必須接收多少個節點以及是否應該將其寫入磁盤:

    • quorum_mem 對應於同步複製,大多數服務器必須接收一條消息並將其存儲在內存中。
    • quorum_disk 更強大,需要將消息寫入磁盤。
    • local_mem是異步複製,其中消息必須僅存儲在內存中。即使刷新了磁盤緩衝區,這也不能保證在服務器重新啟動的情況下傳遞消息。
    • local_disk 是異步複製,其中必須將消息寫入一臺服務器上的磁盤。

    在測試中,我們使用quorum_mem操作群集3節點,發送時,我們創建一個生產者,將交付方式設置為PERSISTENT;接收時,我們創建一個消費者CLIENT_ACKNOWLEDGE,並通過手動確認消息。

    在性能方面,ActiveMQ的性能比RabbitMQ差一點,通過同步複製最多可達到3857 msgs / s

    。這似乎是最大的,並通過1個節點和25個線程來實現:

    常用高可靠消息隊列(MQ)系統選型

    在最高吞吐量下,接收速率在37004100 msg / s之間相當穩定。處理等待時間也較高,最多284 ms

    常用高可靠消息隊列(MQ)系統選型

    添加更多節點並不能改善結果,實際上,它們會稍差一些。有趣的是,使用更強的quorum_disk保證對性能沒有太大影響:

    常用高可靠消息隊列(MQ)系統選型

    常用高可靠消息隊列(MQ)系統選型

    與RabbitMQ相同,在所有測試中,發送和接收均以相同的速率進行,因此,吞吐量方面的瓶頸是發送方。

    Kafka

    常用高可靠消息隊列(MQ)系統選型

    Kafka採用了不同的消息傳遞方法。服務器本身是一個流式發佈-訂閱系統,或者在更基本的層次上,是一個分佈式日誌。每個Kafka主題可以有多個分區。通過使用更多分區,可以拓展消息的使用者(和吞吐量)並增加處理的併發性。

    在具有分區的發佈-訂閱之上,通過將大量邏輯控制使用者中來構建點對點消息傳遞系統。

    消費者組中的每個消費者都從多個獨立分區中讀取消息;因此,增加更多的消費者線程而不增加分區是沒有意義的。

    消息不會在服務器上確認,而是由消費者管理消息偏移量,並將其寫回到特殊的Kafka存儲中,這些存儲可以在後臺自動進行,或手動。

    這樣的設計有兩個後果:

    • 來自相同分區的消息按順序處理。可以自定義分區路由策略
    • 所有消費者都應以相同的速度消費消息。來自慢消費者的消息不會被快速消費者處理
    • 消息不能被選擇性地確認。
    • 沒有可用的“高級”傳遞選項,例如路由或延遲消息傳遞。

    為了實現有保證的發送和最少一次的傳遞,我們使用了以下配置(請參閱KafkaMq類):

    • topic 的 replication-factor 設置 3
    • 對於發送者,request.required.acks選項設置為1(異步複製-發送請求將被阻止,直到被接受為止)或-1(同步複製;與min.insync.replicas主題配置一起設置為2 發送請求將被阻止,直到至少被2 個節點接受為止)。
    • 消費者補償是每10秒手動提交一次;在此期間,消息接收被阻止(使用讀寫鎖來確保)。這樣,我們可以實現至少一次傳遞。

    測試結果:Kafka的表現很棒。使用同步複製,單節點單線程可達到約2391 msgs / s,最好的結果是54494 msgs / s,具有25個發送和接收線程以及6個客戶端發送方/接收方節點。

    常用高可靠消息隊列(MQ)系統選型

    接收速率非常穩定。處理延遲的第95個百分點也是47 ms的穩定值。發送延遲也約為48ms。結果的摘要:

    常用高可靠消息隊列(MQ)系統選型

    常用高可靠消息隊列(MQ)系統選型

    在6個以上的節點上添加更多的客戶端線程並不會提高性能-這可能是我們可以從3節點的Kafka集群中獲得的最大收益。但是,可以添加節點並增加分區數量,Kafka具有很大的可擴展性。

    擴大批次:通過使用多達100個的批次,我們可以通過4個客戶端節點達到102170 msgs / s,而使用多達1000個消息的批次,則可以達到141250 msgs / s。但是,處理延遲然後增加到443ms。

    總結

    選擇哪個消息隊列取決於特定的項目要求,總結如下:

    • 如果您已經在使用Mongo,則很容易在其上構建複製的消息隊列,而無需創建和維護單獨的消息傳遞集群。
    • 如果您想獲得高持久性保證,RabbitMQ可以確保跨集群複製以及在消息發送時在磁盤上進行復制。這是在許多項目中使用的非常受歡迎的選擇,具有完整的AMQP支持。
    • Kafka以功能集為代價提供最佳性能和可擴展性,僅查看吞吐量時,Kafka無疑是贏家。
    • 發送延遲的第95個百分位數始終很低(大約50毫秒),只有較高負載的Rabbit例外。
    • 處理延遲方面,ActiveMQ在高負載下的表現較差。

    當然,除了性能以外,還有許多其他方面,在選擇消息隊列時還應考慮這些方面:例如管理開銷,分區容限,路由功能。希望能夠為您的選擇提供幫助!


    分享到:


    相關文章: