Pulsar VS. Kafka(1): 統一的訊息消費模型(Queue + Stream)

ApachePulsar發表於2021-11-27
原創:Sijie Guo

翻譯:翟佳

之前的文章,我們描述了 Apache Pulsar 能夠成為企業級流和訊息系統的原因。Pulsar 的企業特性包括訊息的持久化儲存,多租戶,多機房互聯互備,加密和安全性等。我們經常被問到的一個問題是 Apache Pulsar 和 Apache Kafka 有什麼不同。

在本系列的Pulsar和Kafka比較文章中,我們將引導您認識和了解訊息系統中一些重要關注點,比如健壯性,高可用性和高頻寬低延遲等。

在使用者選擇一個訊息系統時,訊息模型是使用者首先考慮的事情。訊息模型應涵蓋以下3個方面:

  1. 訊息消費 - 如何傳送和消費訊息;
  2. 訊息確認(ack) - 如何確認訊息;
  3. 訊息儲存 - 訊息保留多長時間,觸發訊息刪除的原因以及怎樣刪除;

訊息消費模型

在實時流式架構中,訊息傳遞可以分為兩類:佇列(Queue)和流(Stream)。

佇列(Queue)模型

佇列模型主要是採用無序或者共享的方式來消費訊息。通過佇列模型,使用者可以建立多個消費者從單個管道中接收訊息;當一條訊息從佇列傳送出來後,多個消費者中的只有一個(任何一個都有可能)接收和消費這條訊息。訊息系統的具體實現決定了最終哪個消費者實際接收到訊息。

佇列模型通常與無狀態應用程式一起結合使用。無狀態應用程式不關心排序,但它們確實需要能夠確認(ack)或刪除單條訊息,以及儘可能地擴充套件消費並行性的能力。典型的基於佇列模型的訊息系統包括 RabbitMQ 和 RocketMQ。

流式(Stream)模型

相比之下,流模型要求訊息的消費嚴格排序或獨佔訊息消費。對於一個管道,使用流式模型,始終只會有一個消費者使用和消費訊息。消費者按照訊息寫入管道的確切順序接收從管道傳送的訊息。

流模型通常與有狀態應用程式相關聯。有狀態的應用程式更加關注訊息的順序及其狀態。訊息的消費順序決定了有狀態應用程式的狀態。訊息的順序將影響應用程式處理邏輯的正確性。

在面向微服務或事件驅動的體系結構中,佇列模型和流模型都是必需的。

Pulsar 的訊息消費模型

Apache Pulsar 通過“訂閱”,抽象出了統一的: producer-topic-subscription-consumer 消費模型。Pulsar 的訊息模型既支援佇列模型,也支援流模型。

在 Pulsar 的訊息消費模型中,Topic 是用於傳送訊息的通道。每一個 Topic 對應著 Apache BookKeeper 中的一個分散式日誌。釋出者釋出的每條訊息只在Topic中儲存一次;儲存的過程中,BookKeeper 會將訊息複製儲存在多個儲存節點上;Topic 中的每條訊息,可以根據消費者的訂閱需求,多次被使用,每個訂閱對應一個消費者組(Consumer Group)。

主題(Topic)是消費訊息的真實來源。儘管訊息僅在主題(Topic)上儲存一次,但是使用者可以有不同的訂閱方式來消費這些訊息:

  • 消費者被組合在一起以消費訊息,每個消費組是一個訂閱。
  • 每個 Topic 可以有不同的消費組。
  • 每組消費者都是對主題的一個訂閱。
  • 每組消費者可以擁有自己不同的消費方式: 獨佔(Exclusive),故障切換(Failover)或共享(Share)。

Pulsar通過這種模型,將佇列模型和流模型這兩種模型結合在了一起,提供了統一的API介面。 這種模型,既不會影響訊息系統的效能,也不會帶來額外的開銷,同時還為使用者提供了更多靈活性,方便使用者程式以最匹配模式來使用訊息系統。

獨佔訂閱(Stream流模型)

顧名思義,獨佔訂閱中,在任何時間,一個消費者組(訂閱)中有且只有一個消費者來消費 Topic 中的訊息。下圖是獨佔訂閱的示例。在這個示例中有一個有訂閱A的活躍消費者A-0,訊息 m0 到 m4 按順序傳送並由 A-0 消費。如果另一個消費者 A-1 想要附加到訂閱A,則是不被允許的。

故障切換(Stream流模型)

使用故障切換訂閱,多個消費者(Consumer)可以附加到同一訂閱。 但是,一個訂閱中的所有消費者,只會有一個消費者被選為該訂閱的主消費者。 其他消費者將被指定為故障轉移消費者。

當主消費者斷開連線時,分割槽將被重新分配給其中一個故障轉移消費者,而新分配的消費者將成為新的主消費者。 發生這種情況時,所有未確認(ack)的訊息都將傳遞給新的主消費者。 這類似於 Apache Kafka 中的 Consumer partition rebalance。

下圖是故障切換訂閱的示例。 消費者 B-0 和 B-1 通過訂閱 B 訂閱消費訊息。B-0 是主消費者並接收所有訊息。 B-1 是故障轉移消費者,如果消費者 B-0 出現故障,它將接管消費。

共享訂閱(Queue佇列模型)

使用共享訂閱,在同一個訂閱背後,使用者按照應用的需求掛載任意多的消費者。 訂閱中的所有訊息以迴圈分發形式傳送給訂閱背後的多個消費者,並且一個訊息僅傳遞給一個消費者。

當消費者斷開連線時,所有傳遞給它但是未被確認(ack)的訊息將被重新分配和組織,以便傳送給該訂閱上剩餘的剩餘消費者。

下圖是共享訂閱的示例。 消費者 C-1,C-2 和 C-3 都在同一主題上消費訊息。 每個消費者接收大約所有訊息的 1/3。

如果想提高消費的速度,使用者不需要不增加分割槽數量,只需要在同一個訂閱中新增更多的消費者。

三種訂閱模式的選擇

獨佔和故障切換訂閱,僅允許一個消費者來使用和消費,每個對主題的訂閱。這兩種模式都按主題分割槽順序使用訊息。它們最適用於需要嚴格訊息順序的流(Stream)用例。

共享訂閱允許每個主題分割槽有多個消費者。同一訂閱中的每個消費者僅接收主題分割槽的一部分訊息。共享訂閱最適用於不需要保證訊息順序的佇列(Queue)的使用模式,並且可以按照需要任意擴充套件消費者的數量。

Pulsar 中的訂閱實際上與 Apache Kafka 中的 Consumer Group 的概念類似。建立訂閱的操作很輕量化,而且具有高度可擴充套件性,使用者可以根據應用的需要建立任意數量的訂閱。對同一主題的不同訂閱,也可以採用不同的訂閱型別。比如使用者可以在同一主題上可以提供一個包含3個消費者的故障切換訂閱,同時也提供一個包含20個消費者的共享訂閱,並且可以在不改變分割槽數量的情況下,向共享訂閱新增更多的消費者。下圖描繪了一個包含3個訂閱 A,B 和 C 的主題,並說明了訊息如何從生產者流向消費者。

除了統一訊息API之外,由於Pulsar主題分割槽實際上是儲存在 Apache BookKeeper 中,它還提供了一個讀取 API(Reader),類似於消費者API(但 Reader 沒有遊標管理),以便使用者完全控制如何使用 Topic 中的訊息。

Pulsar的訊息確認(ACK)

由於分散式系統的特性,當使用分散式訊息系統時,可能會發生故障。比如在消費者從訊息系統中的主題消費訊息的過程中,消費訊息的消費者和服務於主題分割槽的訊息代理(Broker)都可能發生錯誤。訊息確認(ACK)的目的就是保證當發生這樣的故障後,消費者能夠從上一次停止的地方恢復消費,保證既不會丟失訊息,也不會重複處理已經確認(ACK)的訊息。在 Apache Kafka 中,恢復點通常稱為 Offset,更新恢復點的過程稱為訊息確認或提交 Offset。

在 Apache Pulsar 中,每個訂閱中都使用一個專門的資料結構--遊標(Cursor)來跟蹤訂閱中的每條訊息的確認(ACK)狀態。每當消費者在主題分割槽上確認訊息時,遊標都會更新。更新遊標可確保消費者不會再次收到訊息。

Apache Pulsar 提供兩種訊息確認方法,單條確認(Individual Ack)和累積確認(Cumulative Ack)。通過累積確認,消費者只需要確認它收到的最後一條訊息。主題分割槽中的所有訊息(包括)提供訊息ID將被標記為已確認,並且不會再次傳遞給消費者。累積確認與 Apache Kafka 中的 Offset 更新類似。

Apache Pulsar可以支援訊息的單條確認,也就是選擇性確認。消費者可以單獨確認一條訊息。 被確認後的訊息將不會被重新傳遞。下圖說明了單條確認和累積確認的差異(灰色框中的訊息被確認並且不會被重新傳遞)。在圖的上半部分,它顯示了累計確認的一個例子,M12 之前的訊息被標記為acked。在圖的下半部分,它顯示了單獨進行 acking 的示例。僅確認訊息 M7 和 M12 - 在消費者失敗的情況下,除了 M7 和 M12 之外,其他所有訊息將被重新傳送。

獨佔訂閱或故障切換訂閱的消費者能夠對訊息進行單條確認和累積確認;共享訂閱的消費者只允許對訊息進行單條確認。單條確認訊息的能力為處理消費者故障提供了更好的體驗。對於某些應用來說,處理一條訊息可能需要很長時間或者非常昂貴,防止重新傳送已經確認的訊息非常重要。

這個管理Ack的專門的資料結構--遊標(Cursor),由 Broker 來管理,利用 BookKeeper 的 Ledger 提供儲存,在後面的文章中我們會介紹更多的關於遊標(Cursor)的細節。

Apache Pulsar 提供了靈活的訊息消費訂閱型別和訊息確認方法,通過簡單的統一的 API,就可以支援各種訊息和流的使用場景。

Pulsar的訊息保留(Retention)

在訊息被確認後,Pulsar的Broker 會更新對應的遊標。當 Topic 裡面中的一條訊息,被所有的訂閱都確認 ack 後,才能刪除這條訊息。Pulsar還允許通過設定保留時間,將訊息保留更長時間,即使所有訂閱已經確認消費了它們。下圖說明了如何在有2個訂閱的主題中保留訊息。訂閱 A 在 M6 和訂閱 B 已經消耗了 M10 之前的所有訊息之前已經消耗了所有訊息。這意味著M6之前的所有訊息(灰色框中)都可以安全刪除。訂閱A仍未使用 M6 和 M9 之間的訊息,無法刪除它們。如果主題配置了訊息保留期,則訊息M0到M5將在配置的時間段內保持不變,即使 A 和 B 已經確認消費了它們。

在訊息保留策略中,Pulsar 還支援訊息生存時間(TTL)。如果訊息未在配置的TTL時間段內被任何消費者使用,則訊息將自動標記為已確認。 訊息保留期訊息 TTL 之間的區別在於:訊息保留期作用於標記為已確認並設定為已刪除的訊息,而 TTL 作用於未 ack 的訊息。 上面的圖例中說明了 Pulsar 中的TTL。 例如,如果訂閱 B 沒有活動消費者,則在配置的 TTL 時間段過後,訊息 M10 將自動標記為已確認,即使沒有消費者實際讀取該訊息。

Pulsar VS. Kafka

通過以上幾個方面,我們對 Pulsar 和Kafka在訊息模型方面的不同點進行一個總結。

模型概念

Kafka: Producer - topic - consumer group - consumer;

Pulsar:Producer - topic - subscription - consumer。

消費模式

Kafka: 主要集中在流(Stream)模式,對單個 partition 是獨佔消費,沒有共享(Queue)的消費模式;

Pulsar:提供了統一的訊息模型和API。流(Stream)模式 -- 獨佔和故障切換訂閱方式;佇列(Queue)模式 -- 共享訂閱的方式。

訊息確認(Ack)

Kafka: 使用偏移 Offset;

Pulsar:使用專門的 Cursor 管理。累積確認和 Kafka 效果一樣;提供單條或選擇性確認。

訊息保留

Kafka:根據設定的保留期來刪除訊息。有可能訊息沒被消費,過期後被刪除。 不支援 TTL。

Pulsar:訊息只有被所有訂閱消費後才會刪除,不會丟失資料。也允許設定保留期,保留被消費的資料。支援 TTL。

對比總結:

Apache Pulsar 將高效能的流(Apache Kafka所追求的)和靈活的傳統佇列(RabbitMQ 所追求的)結合到一個統一的訊息模型和 API 中。 Pulsar 使用統一的 API 為使用者提供一個支援流和佇列的系統,且具有同樣的高效能。

總結

在這篇部落格文章中,我們介紹了 Apache Pulsar 的訊息模型,該模型將佇列和流式傳輸統一到一個 API 中。應用程式可以將此統一的 API 用於高效能佇列和流式傳輸,而無需維護兩套系統:RabbitMQ 進行佇列處理,Kafka 進行流式處理。希望這篇文章能讓您瞭解 Apache Pulsar 中的訊息模型,訊息消費,刪除和保留是如何工作的;瞭解 Pulsar 和Kafka訊息模型之間的區別。在後面一篇文章中,我們將向您介紹 Apache Pulsar 的架構細節以及 Pulsar 與 Apache Kafka 在資料分發,複製,可用性和永續性方面的差異。


如果對Pulsar感興趣,可通過下列方式參與 Pulsar 社群:

有關 Apache Pulsar 專案的常規資訊,請訪問官網:http://pulsar.incubator.apach...此外也可關注 Twitter 帳號@apache_pulsar。

相關文章