[架構選型 】 全面瞭解Kafka和RabbitMQ選型(1) -兩種不一樣的消息傳遞方式

轉載https://cloud.tencent.com/developer/article/1399226  

在這一部分中,咱們將探討RabbitMQ和Apache Kafka以及它們的消息傳遞方法。每種技術在設計的每一個方面都作出了大相徑庭的決定,每種方面都有優勢和缺點。咱們不會在這一部分得出任何有力的結論,而是將其視爲技術的入門,以便咱們能夠深刻探討該系列的後續部分。安全

RabbitMQ

RabbitMQ是一個分佈式消息隊列系統。分佈式,由於它一般做爲節點集羣運行,其中隊列分佈在節點上,並可選擇複製以實現容錯和高可用性。它原生地實現了AMQP 0.9.1,並經過插件提供其餘協議,如STOMP,MQTT和HTTP。服務器

RabbitMQ同時採用經典和新穎方式。從某種意義上來講,它是面向消息隊列的經典,而且具備高度靈活的路由功能。正是這種路由功能纔是其殺手級功能。構建快速,可擴展,可靠的分佈式消息傳遞系統自己就是一項成就,但消息路由功能使其在衆多消息傳遞技術中脫穎而出。微信

交換機(exchanges)和隊列markdown

超簡化概述:網絡

  • 發佈者向交換機(exchanges)發送消息
  • 將消息路由到隊列和其餘交換機(exchanges)
  • RabbitMQ在收到消息時向發佈者發送確認
  • 消費者與RabbitMQ保持持久的TCP鏈接,並聲明他們使用哪一個隊列
  • RabbitMQ將消息推送給消費者
  • 消費者發送成功/失敗的確認
  • 成功使用後,消息將從隊列中刪除

隱藏在該列表中的是開發人員和管理員應該採起的大量決策,以得到他們想要的交付保證,性能特徵等,咱們將在本系列的後續部分中介紹全部這些決策。架構

咱們來看看單個發佈者,交換機(exchanges),隊列和消費者:分佈式

Fig 1 - Single publisher and single consumer

若是您有多個同一消息的發佈者怎麼辦? 若是咱們有多個消費者每一個人都但願消費每條消息呢?函數

Fig 2 - Multiple publishers, multiple independent

如您所見,發佈者將其消息發送到同一個交換機(exchanges),該交換機(exchanges)將每條消息路由到三個隊列,每一個隊列都有一個消費者。 使用RabbitMQ,隊列使不一樣的消費者可以使用每條消息。 與下圖對比:性能

Fig 3 - Multiple publishers, one queue with multip

在圖3中,咱們有三個消費者都在單個隊列中消費。 這些是競爭的消費者,即他們競爭消費單個隊列的消息。 人們能夠預期,平均而言,每一個消費者將消耗該隊列消息的三分之一。 咱們使用競爭消費者來擴展咱們的消息處理,使用RabbitMQ它很是簡單,只需按需添加或刪除消費者。 不管您擁有多少競爭消費者,RabbitMQ都將確保消息僅傳遞給單個消費者。ui

咱們能夠將圖2和圖3組合在一塊兒,使多組競爭消費者,每組消費每條消息。

Fig 4 - Multiple publishers, multiple queues with

交換和隊列之間的箭頭稱爲綁定,咱們將仔細研究本系列第2部分中的箭頭。

擔保

RabbitMQ提供「最多一次交付」和「至少一次交付」但不提供「徹底一次交付」保證。咱們將在本系列的第4部分中深刻研究消息傳遞保證。

消息按照到達隊列的順序傳遞(畢竟是隊列的定義)。當您擁有競爭消費者時,這並不能保證完成與徹底相同順序的消息處理匹配。這不是RabbitMQ的錯,而是並行處理有序消息集的基本現實。經過使用Consistent Hashing Exchange能夠解決此問題,您將在下一部分中看到模式和拓撲。

推和消費者預選

RabbitMQ將消息推送到流中的消費者。有一個Pull API,但它的性能很糟糕,由於每條消息須要一個請求/響應往返(注意,因爲Shiva Kumar的評論,我更新了這一段)。

若是消息到達隊列的速度快於消費者能夠處理的速度,那麼基於推送的系統可能會使消費者感到壓力。所以,爲了不這種狀況,每一個消費者均可以配置預取限制(也稱爲QoS限制)。這基本上是消費者在任什麼時候候均可以擁有的未確認消息的數量。當消費者開始落後時,這能夠做爲安全切斷開關。

爲何推而不拉?首先,它對於低延遲很是有用。其次,理想狀況下,當咱們擁有單個隊列的競爭消費者時,咱們但願在它們之間均勻分配負載。若是每一個消費者都會收到消息,那麼根據他們拉動工做分佈的數量,可能會變得很是不平衡。消息分佈越不均勻,延遲越多,處理時消息順序的丟失越多。所以,RabbitMQ的Pull API只容許一次提取一條消息,但這會嚴重影響性能。這些因素使RabbitMQ傾向於推進機制。這是RabbitMQ的縮放限制之一。經過將確認組合在一塊兒能夠改善它。

路由

交換基本上是到隊列和/或其餘交換的消息的路由器。爲了使消息從交換機傳送到隊列或其餘交換機,須要綁定。不一樣的交換須要不一樣的綁定。有四種類型的交換和相關綁定:

  • 扇出(Fanout)。路由到具備綁定到交換的全部隊列和交換。標準的pub子模型。
  • 直接。根據發佈者設置的消息隨附的路由密鑰路由消息。路由鍵是一個短字符串。直接交換將消息路由到具備與路由密鑰徹底匹配的綁定密鑰的隊列/交換機。
  • 話題。根據路由密鑰路由消息,但容許通配符匹配。
  • 頭。 RabbitMQ容許將自定義標頭添加到消息中。標頭根據這些標頭值交換路由消息。每一個綁定包括徹底匹配標頭值。能夠將多個值添加到具備匹配所需的ANY或ALL值的綁定。
  • 一致的哈希。這是一個哈希路由密鑰或郵件頭並僅路由到一個隊列的交換。當您須要使用擴展的消費者處理訂單保證時,這很是有用。
Fig 5. Topic exchange example

咱們將在第2部分中更仔細地研究路由,但上面是主題交換的示例。發佈者使用路由密鑰格式LEVEL.AppName發佈錯誤日誌。

  • 隊列1將使用多字#通配符接收全部消息。
  • 隊列2將接收ECommerce.WebUI應用程序的任何日誌級別。它使用覆蓋日誌級別的單字*通配符。
  • 隊列3將查看來自任何應用程序的全部ERROR級別消息。它使用多字#通配符來覆蓋全部應用程序。

經過四種路由消息的方式,以及容許交換路由到其餘交換,RabbitMQ提供了一組功能強大且靈活的消息傳遞模式。接下來咱們將討論死信交換,短暫交換和隊列,您將開始看到RabbitMQ的強大功能。

死信交換機(Dead Letter Exchanges)

咱們能夠配置隊列在如下條件下向交換機發送消息:

  • 隊列超過配置的消息數。
  • 隊列超出配置的字節數。
  • 消息生存時間(TTL)已過時。發佈者能夠設置消息的生命週期,隊列也能夠有消息TTL。哪一個更短適用。

咱們建立一個綁定到死信交換的隊列,這些消息將存儲在那裏直到採起行動。在另外一篇文章中,我描述了我已經實現的拓撲,其中全部死信的消息都發送到中央清算所,支持團隊能夠在此決定採起何種措施。

與許多RabbitMQ功能同樣,死信交換提供了最初未考慮的額外模式。咱們可使用消息TTL和死信交換來實現延遲隊列和重試隊列,包括指數退避。請參閱我以前的帖子。

短暫的交流和隊列(Ephemeral Exchanges and Queues)

能夠動態建立交換和隊列,並賦予自動刪除特徵。通過一段時間後,他們能夠自我毀滅。這容許諸如用於基於消息的RPC的ephermal回覆隊列之類的模式。

插件

您要安裝的第一個插件是Management Plug-In,它提供HTTP服務器,Web UI和REST API。它很是易於安裝,併爲您提供易於使用的UI,以幫助您啓動和運行。經過REST API進行腳本部署也很是簡單。

其餘一些插件包括:

  • 一致的哈希交換,Sharding Exchange等
  • 像STOMP和MQTT這樣的協議
  • 網絡鉤子
  • 額外的交換類型
  • SMTP集成

RabbitMQ還有不少東西,但這是一本很好的入門書,讓您瞭解RabbitMQ能夠作些什麼。如今咱們來看看Kafka,它採用了徹底不一樣的消息傳遞方法,而且具備驚人的功能。

Apache Kafka

Kafka是一個分佈式複製的提交日誌。 Kafka沒有隊列的概念,由於它主要用做消息系統,因此最初可能看起來很奇怪。長期以來,隊列一直是消息傳遞系統的代名詞。讓咱們分解一下「分佈式,複製的提交日誌」:

  • 分佈式,由於Kafka被部署爲節點集羣,用於容錯和擴展
  • 複製,由於消息一般跨多個節點(服務器)複製。
  • 提交日誌由於消息存儲在分區中,因此只追加稱爲主題的日誌。這種日誌概念是Kafka的主要殺手特徵。

瞭解日誌(主題)及其分區是理解Kafka的關鍵。那麼分區日誌與一組隊列有什麼不一樣呢?讓咱們想象一下吧。

Fig 6 One producer, one partition, one consumer

Kafka不是將消息放入FIFO隊列並跟蹤像RabbitMQ那樣在隊列中跟蹤該消息的狀態,而是將其附加到日誌中,就是這樣。不管消耗一次仍是一千次,該消息都會保留。它根據數據保留策略(一般是窗口時間段)刪除。那麼主題如何被消費?每一個消費者跟蹤它在日誌中的位置,它有一個指向消耗的最後消息的指針,該指針稱爲偏移量。消費者經過客戶端庫維護此偏移量,而且根據Kafka的版本,偏移量存儲在ZooKeeper或Kafka自己中。 ZooKeeper是一種分佈式共識技術,被許多分佈式系統用於領導者選舉等領域。 Kafka依靠ZooKeeper來管理集羣的狀態。

這個日誌模型的驚人之處在於它當即消除了消息傳遞狀態的大量複雜性,更重要的是消費者,它容許它們倒回並返回並消耗先前偏移量的消息。例如,假設您部署了一個計算髮票的服務,該發票消耗了客戶預訂。該服務有一個錯誤,並在24小時內錯誤地計算全部發票。最好使用RabbitMQ,您須要以某種方式從新發布這些預訂,並僅發送給發票服務。可是對於Kafka,您只需將該消費者的偏移量移回24小時。

所以,讓咱們看一下具備單個分區和兩個消費者的主題的狀況,每一個消費者都須要消費每條消息。從如今開始,我已經開始爲消費者貼上標籤,由於它不是那麼清晰(如RabbitMQ圖),它們是獨立的,也是競爭對手的消費者。

Fig 7 One producer, one partition, two independent

從圖中能夠看出,兩個獨立的消費者都使用相同的分區,但他們正在從不一樣的偏移中讀取。 也許發票服務處理消息所需的時間比推送通知服務要長,或者發票服務可能會停機一段時間而且遇上,或者可能存在錯誤而且其偏移量必須移回幾個小時。

如今讓咱們說發票服務須要擴展到三個實例,由於它沒法跟上消息速度。 使用RabbitMQ,咱們只需部署兩個發票服務應用程序,這些應用程序將使用預訂發票服務隊列。 可是Kafka不支持單個分區上的競爭消費者,Kafka的並行單元就是分區自己。 所以,若是咱們須要三個發票消費者,咱們至少須要三個分區。 因此如今咱們有:

Fig 8 Three partitions and two sets of three consu

所以,這意味着您至少須要與最大規模的消費者同樣多的分區。咱們來談談分區。

分區和消費者組

每一個分區都是一個單獨的數據文件,可保證消息排序。這一點很重要:消息排序只能保證在一個分區內。這可能會在消息排序需求和性能需求之間引入一些緊張,由於並行單元也是分區。一個分區不能支持競爭消費者,所以咱們的發票應用程序只能有一個實例消耗每一個分區。

消息能夠循環方式或經過散列函數路由到分區:散列(消息密鑰)%分區數。使用散列函數有一些好處,由於咱們能夠設計消息密鑰,使得同一實體的消息(例如預訂)始終轉到同一分區。這能夠實現許多模式和消息排序保證。

消費者羣體就像RabbitMQ的競爭消費者。組中的每一個使用者都是同一應用程序的實例,並將處理主題中全部消息的子集。儘管RabbitMQ的競爭消費者都使用相同的隊列,但消費者羣體中的每一個消費者都使用同一主題的不一樣分區。所以,在上面的示例中,發票服務的三個實例都屬於同一個使用者組。

在這一點上,RabbitMQ看起來更加靈活,它保證了隊列中的消息順序,以及它應對不斷變化的競爭消費者數量的無縫能力。使用Kafka,如何對日誌進行分區很是重要。

Kafka從一開始就有一個微妙而重要的優點,即RabbitMQ後來添加的關於消息順序和並行性的優勢。 RabbitMQ維護整個隊列的全局順序,但在並行處理該隊列期間沒法維護該順序。 Kafka沒法提供該主題的全局排序,但它確實提供了分區級別的排序。所以,若是您只須要訂購相關消息,那麼Kafka提供有序消息傳遞和有序消息處理。想象一下,您有消息顯示客戶預訂的最新狀態,所以您但願始終按順序(按時間順序)處理該預訂的消息。若是您按預訂ID進行分區,那麼給定預訂的全部消息都將到達單個分區,咱們會在其中進行消息排序。所以,您能夠建立大量分區,使您的處理高度並行化,並得到消息排序所需的保證。

RabbitMQ中也存在此功能,它經過Consistent Hashing交換機以相同的方式在隊列上分發消息。雖然Kafka強制執行此有序處理,由於每一個使用者組只有一個使用者可使用單個分區,而且當協調器節點爲您完成全部工做以確保遵照此規則時,能夠輕鬆實現。而在RabbitMQ中,您仍然可讓競爭消費者從一個「分區」隊列中消費,而且您必須完成工做以確保不會發生這種狀況。

這裏還有一個問題,當你改變分區數量時,訂單Id 1000的那些消息如今轉到另外一個分區,所以訂單Id 1000的消息存在於兩個分區中。根據您處理郵件的方式,這會引發頭疼。如今存在消息不按順序處理的狀況。

咱們將在本系列的第4部分「消息傳遞語義和保證」部分中更詳細地介紹此主題。

PUSH VS PULL

RabbitMQ使用推送模型,並經過消費者配置的預取限制來防止壓倒性的消費者。這對於低延遲消息傳遞很是有用,而且適用於RabbitMQ基於隊列的架構。另外一方面,Kafka使用拉模型,消費者從給定的偏移量請求批量消息。當沒有超出當前偏移量的消息時,爲了不緊密循環,Kafka容許進行長輪詢。

因爲其分區,拉模型對Kafka有意義。因爲Kafka在沒有競爭消費者的分區中保證消息順序,咱們能夠利用消息批處理來實現更高效的消息傳遞,從而爲咱們提供更高的吞吐量。這對RabbitMQ沒有多大意義,由於理想狀況下咱們但願儘量快地分配一個消息,以確保工做均勻並行處理,而且消息處理接近它們到達隊列的順序。可是對於Kafka來講,分區是並行和消息排序的單位,因此這兩個因素都不是咱們關注的問題。

發佈訂閱

Kafka支持基本的pub sub,其中包含一些與日誌相關的額外模式,它是一個日誌並具備分區。生成器將消息附加到日誌分區的末尾,而且消費者能夠在分區中的任何位置放置它們的偏移量。

Fig 9. Consumers with different offsets

當存在多個分區和使用者組時,這種風格的圖表不容易快速解釋,所以對於Kafka的其他圖表,我將使用如下樣式:

Fig 10. One producer, three partitions and one con

咱們的消費者羣體中沒有與分區相同數量的消費者:

Fig 11. Sone consumers read from more than one par

一個消費者組中的消費者將協調分區的消耗,確保一個分區不被同一個消費者組的多個消費者使用。

一樣,若是咱們擁有的消費者多於分區,那麼額外的消費者將保持閒置狀態。

Fig 12. One idle consumer

添加和刪除消費者後,消費者羣體可能會變得不平衡。 從新平衡會在分區中儘量均勻地從新分配使用者。

Fig 13. Addition of new consumers requires rebalan

在如下狀況以後自動觸發從新平衡:

  • 消費者加入消費者羣體
  • 消費者離開消費者羣體(它關閉或被視爲死亡)
  • 添加了新分區

從新平衡將致使短期的額外延遲,同時消費者中止閱讀批量消息並分配到不一樣的分區。消費者維護的任何內存狀態如今均可能無效。 Kafka的消費模式之一是可以將給定實體的全部消息(如給定的預訂)指向同一個分區,從而致使同一個消費者。這稱爲數據局部性。在從新平衡任何內存中有關該數據的數據將是無用的,除非將消費者分配回同一分區。所以,維持國家的消費者須要在外部堅持下去。

日誌壓縮

標準數據保留策略是基於時間和空間的策略。存儲到最後一週的消息或最多50GB,例如。可是存在另外一種類型的數據保留策略 - 日誌壓縮。壓縮日誌時,結果是僅保留每一個消息密鑰的最新消息,其他消息將被刪除。

讓咱們假設咱們收到一條消息,其中包含用戶預訂的當前狀態。每次更改預訂時,都會根據預訂的當前狀態生成新事件。該主題可能包含一些預訂的消息,這些消息表示自建立以來預訂的狀態。在主題被壓縮以後,將僅保留與該預訂相關的最新消息。

根據預訂量和每次預訂的大小,理論上能夠將全部預訂永久存儲在主題中。經過按期壓縮主題,咱們確保每一個預訂只存儲一條消息。

日誌壓縮能夠實現一些不一樣的模式,咱們將在第3部分中探討。

有關消息排序的更多信息

咱們已經討論過,RabbitMQ和Kafka均可以擴展和維護消息排序,可是Kafka使它變得容易多了。使用RabbitMQ,咱們必須使用Consistent Hashing Exchange並使用像ZooKeeper或Consul這樣的分佈式共識服務本身手動實現使用者組邏輯。

但RabbitMQ有一個有趣的功能,卡夫卡沒有。 RabbitMQ自己並不特別,但任何基於發佈 - 訂閱隊列的消息傳遞系統。能力是這樣的:基於隊列的消息系統容許訂戶訂購任意事件組。

讓咱們再深刻了解一下。不一樣的應用程序沒法共享隊列,由於它們會競爭使用消息。他們須要本身的隊列。這使應用程序能夠自由地配置他們認爲合適的隊列。他們能夠將多個主題中的多個事件類型路由到其隊列中。這容許應用程序維護相關事件的順序。它想要組合的事件能夠針對每一個應用程序進行不一樣的配置。

使用像Kafka這樣的基於日誌的消息傳遞系統是不可能的,由於日誌是共享資源。多個應用程序從同一日誌中讀取。所以,將相關事件分組到單個主題中是在更普遍的系統架構級別作出的決策。

因此這裏沒有勝利者。 RabbitMQ容許您維護任意事件集的相對排序,Kafka提供了一種維持大規模排序的簡單方法。

更新:我已經構建了一個名爲Rebalanser的庫,它爲RabbitMQ for .NET應用程序提供了使用者組邏輯。查看它上面的帖子和GitHub repo。若是人們表現出任何興趣,那麼我就會用其餘語言製做版本。讓我知道。

結論

RabbitMQ因爲其提供的各類功能,提供了瑞士軍刀的消息模式。憑藉其強大的路由功能,它能夠消除消費者在只須要一個子集時檢索,反序列化和檢查每條消息的須要。它易於使用,經過簡單地添加和刪除消費者來完成擴展和縮小。它的插件架構容許它支持其餘協議並添加新功能,例如Consistent散列交換,這是一個重要的補充。

卡夫卡的分佈式日誌與消費者抵消使得時間旅行成爲可能。它可以將相同密鑰的消息按順序路由到同一個消費者,從而實現高度並行化的有序處理。 Kafka的日誌壓縮和數據保留容許RabbitMQ沒法提供的新模式。最後是的,Kafka能夠比RabbitMQ進一步擴展,可是咱們大多數人都處理一個能夠輕鬆處理的消息量。

在下一部分中,咱們將使用RabbitMQ仔細研究消息傳遞模式和拓撲。

本文分享自微信公衆號 - 智能時刻(intelligentinterconn)

原文出處及轉載信息見文內詳細說明,若有侵權,請聯繫 yunjia_community@tencent.com 刪除。

原始發表時間:2019-02-15

本文參與騰訊雲自媒體分享計劃,歡迎正在閱讀的你也加入,一塊兒分享。

發表於 2019-03-06
相關文章
相關標籤/搜索