技術探究:Apache Pulsar 的事務型事件流

ApachePulsar發表於2021-12-01

關於 Apache Pulsar

Apache Pulsar 是 Apache 軟體基金會頂級專案,是下一代雲原生分散式訊息流平臺,集訊息、儲存、輕量化函式式計算為一體,採用計算與儲存分離架構設計,支援多租戶、持久化儲存、多機房跨區域資料複製,具有強一致性、高吞吐、低延時及高可擴充套件性等流資料儲存特性。
GitHub 地址:http://github.com/apache/pulsar/

導語:本文是 StreamNative 開發工程師、Apache Pulsar Committer 叢搏在 Pulsar Summit Asia 2020 大會上的演講《技術探究:Apache Pulsar 的事務型事件流》文字整理版本,本演講主要對 Apache Pulsar 事務原理及規劃進行了分享,請大家參考。

我叫叢搏,是來自 StreamNative 的開發工程師。今天我所帶來的主題是《技術探究:Apache Pulsar 的事務型事件流》。

訊息語義

大家都知道在所有的訊息系統流資料平臺對於訊息都有不同的語義。一般語義分為三種:At-most once、At-least once、Exactly once。

  • At-most once:至多一次,不關心訊息是否傳送成功、不需要訊息傳送的返回值。
  • At-least once:至少一次,允許訊息重複但必須保證訊息必達。
  • Exactly once:精準一次,保證訊息不丟且不會重複。

At Most Once(最多一次)

Pulsar 在 1.2.0 版本之前,已經實現 At Most Once 語義。

At Least Once(至少一次)

Pulsar 在設計之初就遵循 At Least Once 語義。傳送訊息失敗後重試是保證 At Least Once 語義的基本方式。傳送重試會導致訊息重複,在某些使用場景下要求 Producer 不能傳送重複訊息且 Consumer 不能重複消費,因此產生了 Exactly Once 語義。

Exactly Once(精確一次)

實現 Exactly once 需要實現對消費/生產的去重。

Pulsar 中如何去重?

  • Producer: 冪等性 Producer;
  • Broker: 保證訊息重複資料刪除 ( PIP-6 );
  • Consumer: Reader + Checkpoints (Flink / Spark)。

如何開啟 Exactly once?

對 topic 的 name space 進行 set-deduplication 的設定。通過 admin 等等的一些操作:

  • bin/pulsar-admin set-deduplication -e tenant/namespace
  • 建立 Producer 時設定 Producer name、Sequence id;
  • 產生訊息時指定遞增的序列 id。

限制:

  • 僅在向一個分割槽生成訊息時有效;
  • 僅適用於產生一條訊息;
  • 在一個分割槽或多個分割槽上生成多個訊息時沒有原子性;
  • Consumer 需要儲存 message id 及其狀態,並在還原狀態時查詢 message id。

Transaction 如何處理事件

通過轉賬的邏輯操作的例子來講述流訊息系統中 Transaction 是怎樣處理事件的:

現在有 Alice 和 Bob 兩個人。Alice 會給 Bob 轉賬十塊錢。通過 Pulsar 如何來實現這個功能呢?

  • Transfer Topic : 記錄轉賬的請求;
  • Cash Transfer Function: 處理轉賬的行為;
  • BalanceUpdate Topic: 記錄餘額更新請求。

Alice 轉給 Bob。Transfer Function 收到這條轉賬訊息,會向 BalanceUpdate Topic 傳送一條 Bob 餘額增加十塊錢的訊息,向 BalanceUpdate Topic 傳送一條 Alice 餘額減少十塊錢的訊息。接收到所有返回值後,Ack 這條轉賬訊息。在所有的操作不會發生任何失敗的時候它是沒有問題的。但是往往事與願違,它的所有操作都可能會發生問題。

圖 1

如圖 1 所示,Ack 失敗後會重新再消費這條轉賬的訊息,所帶來的後果就是 Alice 再次給 Bob 轉了十塊錢,Alice 共給 Bob 轉了二十塊錢。倘若每次 Ack 都失敗,有可能 Alice 的賬戶負債累累,Bob 成了億萬富翁。

圖 2

如圖 2 所示,Bob 增加餘額的訊息沒有成功傳送到所對應的 BalanceUpdate Topic 中,所帶來的現象是 Bob 餘額沒有增加,Alice 的餘額卻減少了。

Pulsar Transaction

如何用 Pulsar 的 Transaction 來實現這件事情的?

Transaction 語義:

  • 保證多分割槽原子性訊息寫入;
  • 保證原子性確認多個訂閱;
  • 一個事務中進行的所有操作全部成功或全部失敗;
  • 允許 Consumers 讀取已提交的訊息。

沒有 Transaction API 如何實現上述的例子?

Message<String> message = inputConsumer.receive();
 
CompletableFuture<MessageId> sendFuture1 =
producer1.newMessage().value(“output-message-1”).sendAsync();
CompletableFuture<MessageId> sendFuture2 =
producer2.newMessage().value(“output-message-2”).sendAsync();
 
inputConsumer.acknowledgeAsync(message.getMessageId());

如圖 3 所示:

圖 3

從 Input Consumer 中接收到訊息之後,Producer1 會傳送訊息到 topic1 中,Producer2 會傳送一條訊息到 topic2,然後 Ack 接收到的訊息。

Pulsar 的 Transaction API 其實很簡單,對於原有需實現的邏輯沒有太大的改變:

Message<String> message = inputConsumer.receive();
Transaction txn = client.newTransaction().withTransactionTimeout(…).build().get();
 
CompletableFuture<MessageId> sendFuture1 =
producer1.newMessage(txn).value(“output-message-1”).sendAsync();
CompletableFuture<MessageId> sendFuture2 =
producer2.newMessage(txn).value(“output-message-2”).sendAsync();
inputConsumer.acknowledgeAsync(message.getMessageId(), txn);
 
txn.commit().get();
 
MessageId msgId1 = sendFuture1.get();
MessageId msgId2 = sendFuture2.get();
 
inputConsumer.acknowledgeAsync(message.getMessageId(), txn);
 
txn.commit().get();

Pulsar Transaction 有如下三大元件:

  • TC (Transaction Coordinator)負責管理 Transaction 後設資料。
  • TB (Transaction Buffer)負責處理髮送帶有 Transaction 的訊息。
  • TP (Transaction Pending Ack)負責處理帶有 Transaction 的 Ack 請求。

圖 4

如圖 4 所示:建立 Transaction 的操作記錄在 TC 中。

圖 5

如圖 5 所示:Pulsar Client 已成功建立 Txn1,並向 TC 請求 Txn1 將傳送訊息到 Topic1 和 Topic2,TC 收到傳送請求後記錄傳送後設資料並響應 Client。Client 向 Topic1,Topic2 分別傳送一條訊息。

圖 6

如圖 6 所示:與圖 5 所描述基本相同。僅傳送與簽收的區別。

圖 7
圖 8

如圖 7、圖 8 所示:Pulsar Client 等待所有 ACK 和 Produce 完成後 Commit Transaction,TC 接收到 Commit 請求後 Txn1 的狀態更改成 Committing 後會處理 TP 和 TB 中 Txn1 的資訊。

圖 9

如圖 9 所示:處理 TP、TB 完成後 TC 會更改 Txn1 的狀態為 Committed。

以上是一個 Transaction 完整的生命週期。

再來看一下轉賬的例子:

圖 10

當有 Pulsar Transaction 的支援後,所有的操作要麼成功,要麼都失敗。就保證了對 Alice 和 Bob 餘額操作的正確性。

Pulsar Transaction 的未來規劃

Pulsar Transaction 的設計目的是讓事件流系統變得更加簡單、可靠性更加強。對於許多業務場景來說,在處理業務場景時就可能會少了很多處理冪等性的操作等等。

那麼,以下就是 Pulsar Transaction 未來的開發規劃:

  • Transaction support in other languages (e.g. C++, Go)
  • Transaction in Pulsar Functions & Pulsar IO
  • Transaction in Kafka-on-Pulsar (KOP)
  • Transaction for Flink / Spark job
  • Transaction for State storage in Pulsar Functions

大家對於上方的內容感興趣歡迎掃描下方二維碼回覆「入群」隨時在 Pulsar 交流群中與我們一起討論。

對於文中的介紹想要了解更多,請掃描下方小程式碼檢視完整版視訊:

相關閱讀

點選連結 ,獲取 Apache Pulsar 硬核乾貨資料!

相關文章