從12個方面詳細解讀Pulsar的主題與訂閱
1、主題(Topics)
Pulsar中主題類似一個URL,格式如下所示:
{persistent|non-persistent}://tenant/namespace/topic
主題的每一個部分說明如下:
persistent|non-persistent 表示持久化或非持久化 tenant 表示租戶 namespace 表示名稱空間 topic 主題的名稱
在Pulsar中你不需要顯示的建立主題,如果當客戶端向一個不存在的主題傳送訊息或訂閱訊息時,Pulsar會自動建立主題。
1.1 名稱空間(Namespaces)
名稱空間是一個租戶下的邏輯概念,名稱空間可以為應用程式管理主題的目錄層次結構。
1.2 訂閱型別(Subscription Types)
在Pulsar中,有四種訂閱型別,它們分別是exclusive、shared、failover和key_shared。四種訂閱模式的圖解如下所示:
在Pulsar中,訂閱模式並不是建立消費者時指定的,而是在消費者啟動時指定的,並可以透過重啟改變訂閱型別,當一個消費組(訂閱)並沒有啟動真實的消費者,這個消費組的訂閱型別是未定義。
接下來對上述四種訂閱模式展開介紹。
1.2.1 獨佔模式(Exclusive)
Exclusive獨佔模式,一個訂閱(對標Kafka/RocketMQ消費組)只允許一個消費者訂閱,如果多個消費者嘗試使用該訂閱去消費訊息會丟擲異常,也就是說一個消費者處理主題的所有分割槽,如下圖所示:
這個是Consumer B啟動時會報錯,只有ConsumerA能收到訊息。
注意:Pulsar預設的訂閱型別為 Exclusive。
1.2.2 主備/故障轉移模式(Failover)
Failover訂閱模式,可以允許多個消費者附加到一個訂閱上。消費者的佇列分配策略根據主題型別有所區別:
如果是分割槽主題(一個主題擁有多個佇列的主題),broker服務端會根據消費者優先順序和消費者名稱字典順序進行排序,然後Broker會將主題中的分割槽平均分配給高優先順序的消費者,低優先順序消費者會成為分割槽的備消費者。 如果是非分割槽主題,Broker按照消費者訂閱主題的順序選擇主消費者,其他的成為備消費者。
非分割槽主題的訂閱示例說明如下:
對於分割槽主題的訂閱圖解說明如下:
溫馨提示:Pulsar的Failover訂閱模式可以類比RocketMQ中的叢集消費模式下的平均分配演算法。
1.2.3 共享/輪詢模式(Shared)
在Shared訂閱模式下,多個消費者可以附加到同一個訂閱,訊息以迴圈分發的方式輪流傳送給各個消費者,並且任何給定的訊息只會傳遞給一個消費者,當一個消費者斷開連線時,所有傳送給它並未被確認的訊息將重新排程,再傳送給其他消費者。
Shared模式的訂閱圖解如下所示:
上圖中的ConsumerA、ConsumerB、ConsumerC都會參與訊息消費。
Shared模式與Failover模式的主要差別是Shared模式並不和消費者繫結佇列,即Shared模式將所有分割槽的訊息當成一個整體來看,
使用Shared模式需要的幾個注意事項:
無法保證訊息的順序性 Shared模式不能使用累積確認機制。
這種模式的最大缺點是不能啟用累積確認機制,訊息確認效率會降低,但其優勢也比較明顯,在解決單個佇列積壓方面,能充分所有消費者的處理能力。
1.2.4 基於Key的共享模式(Key_Shared)
在Key_Shared模式中,多個消費者可以附加到同一個訂閱。具有相同Key的訊息會分發給同一個消費者。
Key_Shared模式的訊息分發機制如下所示:
Pulsar提供了Sticky(粘性)、Auto-split Hash Range(自動分割雜湊範圍)、Auto-split Consistent Hashing(自動分割一致性雜湊)這三種選擇演算法。
選擇消費者的基本過程如下所示:
將分片Key傳遞到一個雜湊函式,生成一個雜湊值 將Key的雜湊值傳入到對應的分片演算法中,從而選擇出一個消費者。
當一個新的消費者加入或者一個消費者退出時,分配演算法都將會重新計算訊息到消費者的對映(選擇)。
接下來分別介紹這三種分配演算法底層的工作機制。
1.2.4.1 Auto-split Hash Range
分段雜湊取模演算法,設定的總區間為0~65536,再根據消費者個數來分配段,然後用消費者的key進行雜湊演算法得出hash值,再用這個hash值域65535取模得到最終的區間值,然後看該值落在哪個區間,就由哪個消費者來消費,示例如下:
下面用圖解的方式說明當新增消費者或減少消費者,分段雜湊取模演算法是如何進行重新對映的:
1.2.4.2 Auto-split Consistent Hashing
一致性雜湊演算法。如果要開啟一致性hash演算法,需要在broker端subscriptionKeySharedUseConsistentHashing設定為true。
1.2.4.3 Sticky
這個類似Auto-split Hash Range,總的區間也是0~65536,與Auto-split Hash Range的區別是每一個消費者對應的區間是固定的,並且由使用者來保證每一個消費者的區間不會重疊。
對應消費者C1的初始化程式碼:
KeySharedPolicySticky keySharedPolicy = KeySharedPolicy.stickyHashRange();
keySharedPolicy.ranges(new Range(0,16384), new Range(32768, 49152));
try (Consumer<String> consumer = client.newConsumer(Schema.STRING)
.topic(Constants.testTopic)
.subscriptionName(Constants.subscriptionName)
.subscriptionType(Constants.subscriptionType)
.subscriptionInitialPosition(Constants.subscriptionInitialPosition)
.keySharedPolicy(keySharedPolicy)
.subscribe()) {
... 省略 ....
Key_Shared訂閱模式要確保同一個Key的訊息在被統一時刻只會傳遞給單一的消費者處理,但當一個消費者新加入時,一些鍵的對映會發生改變,說明如下所示:
為了避免相同key的消費被轉發給不同的消費者,Plusar的處理方法是新消費者建立時與Broker建立連線後,會將新消費者與當前的讀位置【可以理解已經下發給消費者的最大訊息偏移量】關聯起來,只有在讀位置之前的訊息全部確認後,後續訊息才會繼續推送到新的消費者。
但這樣做也會引發一個新的問題:那就是如果一個現有消費者堵塞了,並且沒有定義消費超時,那麼新的消費者將收不到任何訊息,直到被阻塞的消費者恢復或斷開連線。
當然我們也可以在消費端設定 allowOutOfOrderDelivery 為true,放鬆上面的限制,即新的消費者連線后里面可以接受訊息,這樣會短暫的破壞這一原則,在嚴格順序消費場景,會破壞順序語義。
值得注意的是當消費者使用Key_Shared訂閱型別時,需要在訊息傳送端禁用批處理或者啟用積壓Key的批處理機制。
在訊息傳送端啟用基於Key的批處理機制,Java版本的示例程式碼如下:
Producer<byte[]> producer = client.newProducer()
.topic("my-topic")
.batcherBuilder(BatcherBuilder.KEY_BASED)
.create();
在使用Key_Shared訂閱模式時需注意如下幾點:
在訊息傳送時必須為訊息指定一個Key 無法使用累積確認 當主題中最新訊息的位置為X時,預設新的消費者一上線後不會立馬消費訊息,必須等待X之前的訊息全部確認。
1.3 訂閱模式(Subscription modes)
訂閱模式主要是指的遊標型別。Pulsar在建立一個訂閱時,將建立一個遊標來記錄最後消費的位置。當消費者重新啟動時可以繼續從上一次消費位置繼續消費。
Pulsar中定義了Durable、NonDurable兩種訂閱模式:
Durable 遊標持久,如果Broker由於某一個錯誤重啟後,它可以從持久化儲存元件(BookKeeper)中恢復遊標,這樣訊息會從上一次持久的位置開始消費。Durable為預設方式。 NonDurable 遊標非持久化,一旦Broker停止,遊標就會丟失,並且永遠無法再恢復。
客戶端在構建消費者時可以透過如下程式碼改變訂閱模式:
Consumer<byte[]> consumer = pulsarClient.newConsumer()
.topic("my-topic")
.subscriptionName("my-sub")
.subscriptionMode(SubscriptionMode.Durable)
.subscribe();
1.4 多主題訂閱(Multi-topic subscriptions)
當消費者訂閱Pulsar主題時,預設情況下它訂閱一個特定的主題,例如persistent://public/default/my-topic。然而,從Pulsar 1.23.0-incubating版本開始,Pulsar消費者可以同時訂閱多個主題。你可以用兩種方式定義主題列表:
主題名稱可以使用正規表示式,例如persistent://public/default/finance-.*。 可以顯示指定多個主題
溫馨提示:當使用正規表示式訂閱多個主題時,這些主題會限制在同一個名稱空間(Namespace)中。
當符合正規表示式的主題建立後,消費者能夠自動訂閱。當生產者向多個主題傳送訊息時,不能保證訊息在不同主題直接的順序性。
多主題訂閱的使用示例程式碼如下:
PulsarClient pulsarClient = // Instantiate Pulsar client object
// Subscribe to all topics in a namespace
Pattern allTopicsInNamespace = Pattern.compile("persistent://public/default/.*");
Consumer<byte[]> allTopicsConsumer = pulsarClient.newConsumer()
.topicsPattern(allTopicsInNamespace)
.subscriptionName("subscription-1")
.subscribe();
1.5 分割槽主題(Partitioned topics)
普通主題僅僅由一個Broker提供服務,這限制了主題的最大吞吐量。分割槽主題是由多個Broker共同處理的特殊型別的主題,分割槽的主題的示意圖如下所示:
正如上圖中那樣,Topic1擁有5個分割槽(P0,p1,p2,p3,p4),分別分在3個Broker中,其中broker1,broker2上分別建立了2個,而Broker3中建立了3個,關於分割槽在Broker的分佈情況由Pulsar內部根據負載決定其分佈。
訊息傳送者在訊息傳送時如何選擇分割槽由**(Routing mode)路由模式**來決定,Broker如何將訊息推送給消費者(訂閱者)則有訂閱型別來決定。
分割槽主題需要透過管理API顯示建立,分割槽的數量可以在建立主題時指定,具體命令如下:
./pulsar-admin topics create-partitioned-topic persistent://codingw/codingw00/dw_test_03_05_000 --partitions 4
1.6 路由模式(Routing mode)
需要將訊息傳送到分割槽主題時必須指定路由模式(負載均衡演算法),Pulsar中的路由模式由MessageRoutingMode列舉型別定義,其選項說明如下:
RoundRobinPartition 輪詢模式,如果訊息不包含Key,則按批次進行輪詢所有分割槽,如果設定了key,則按Key的雜湊值與分割槽數取模。這個是Pulsar的預設行為。 SinglePartition 如果訊息沒有指定Key,生產者將隨機選擇一個分割槽,一旦分割槽被選擇後,後續所有訊息都將傳送該分割槽;如果指定了Key,則按key的雜湊進行雜湊。 CustomPartition 使用者自定義演算法,需要實現MessageRouter介面。
1.7 順序性保證(ordering guarantee)
訊息的順序性主要取決於訊息的路由模型(MessageRoutingMode)與訊息的key。如果訊息指定了Key,則無論是RoundRobinPartition還是SinglePartition,相同的key的訊息都會傳送到相同的分割槽。
在Pulsar中提供了兩種順序級保證:
Per-key-partition 分割槽級 同一個Key的訊息分佈在一個分割槽中,實現訊息在分割槽級順序,使用技巧:訊息附加Key並採取RoundRobinPartition、SinglePartition路由演算法。 Per-producer 來自同一個訊息傳送者的所有訊息保持順序,使用技巧:每條訊息不設定key並且採用SinglePartition路由演算法。
在Pulsar中提供了JavaStringHash、Murmur3_32Hash兩種Hash演算法,預設為JavaStringHash,但如果客戶端存在多種語言,則推薦使用Murmur3_32Hash。可以透過如下程式碼指定Hash演算法:
Producer<String> producer = pulsarClient.newProducer(Schema.STRING)
.enableBatching(false)
.topic(Constants.testTopic)
.hashingScheme(HashingScheme.JavaStringHash)
.create()
1.8 非持久化主題(Non-persistent topics)
預設情況下Pulsar會將所有未確認的訊息儲存在BookKeeper叢集中。因此Broker發生故障,未確認的訊息可以進行故障轉移。與之對應的是非持久化主題,Pulsar將這部分訊息只儲存在Broker的記憶體中,一旦Broker發生故障而重啟,這塊訊息會丟失。
非持久化主題的字首為:non-persistent,如下所示:
non-persistent://tenant/namespace/topic
對於非持久化主題,訊息只存在Broker的記憶體中,沒有額外的快取區,這意外著Broker接受到訊息生產者訊息後,會立即傳遞給所有連線的消費者,一旦Broker出現異常或者無法從記憶體中檢索訊息資料,則可能會導致訊息丟失,因此需要謹慎使用。
預設情況下非持久化主題在Broker上時開啟的,可以在Pulsar Broker配置檔案中透過修改enableNonPersistentTopics的值為fasle禁用該機制。
non-persistent的主題元資訊不會持久化到Zookeeper,這就意味著如果擁有主題的Broker崩潰,這些非持久化主題的主題無法自動轉移到其他Broker。解決的辦法:Broker的配置檔案中將allowAutoTopicCreation設定為true,並將allowAutoTopicCreationType設定為non-partitioned。
1.9 系統主題(System topic)
系統主題是一個預定義的主題,供Pulsar內部使用。目前Pulsar中的系統主題如下圖所示:
我們可以透過pulsar運維命令 pulsar-admin topics list 命令中增加 -ist或者--include-system-topic選項,用於顯示系統主題。
1.10 訊息重新投遞(Message redelivery)
Apache Pulsar使用至少消費一次的語義。也就是確保訊息至少被消費一次,要想啟用Broker的訊息重新投遞機制,在消費端可以透過如下機制:
Negative Acknowledgment 主動取消確認。 Acknowledgment Timeout 確認超時機制。 Retry letter topic 重試主題。
1.11 訊息保留與過期機制
預設情況下,Pulsar對訊息採取如下機制:
立即刪除已被消費者確認的所有訊息 將所有未確認的消費持久化儲存在backlog中
但我們可以覆蓋上述預設行為:
訊息保留機制(Message retention)使您能夠儲存已被使用者確認的訊息 訊息過期(Message expiry)使您能夠為尚未被確認的訊息設定生存時間(TTL)
關於訊息保留與過期刪除機制,將在後續文章中以專題方式詳細介紹其實現原理,對應官方文件:Message retention and expiry | Apache Pulsar
1.12 訊息重複刪除(Message deduplication)
如果沒有啟用訊息重複刪除機制,下圖展示了訊息被持久化多次的情況:
也就是當訊息傳送者由於超時進行重試時,Broker收到了同一個客戶端多條內容相同的訊息,Broker會將多條內容相同的訊息儲存多次,造成訊息在服務端的重複儲存。
Pulsar支援訊息重複刪除機制,如果開啟了訊息重試機制,其工作示意如下所示:
在Pulsar中,訊息重複刪除機制可以在namespace或主題級別開啟。
預設情況下在Broker、Namespace、Topic都是禁用訊息重複刪除機制的。
我們可以透過如下三種方式開啟訊息重複刪除機制:
在Broker端進行全域性配置
透過pulsar-admin namespaces命令在namespace級別設定
透過pulsar-admin topics 命令在topic級別設定
在broker的配置檔案中我們可以配置如下引數:
brokerDeduplicationEnabled 在Broker是否開啟訊息重複刪除機制,預設為false。如果設定為true,則預設在namespace,topic級別開啟,如果設定為false,則可以單獨在namespace、topic級別啟用或禁用。 brokerDeduplicationMaxNumberOfProducers 設定識別訊息重複刪除所涉及到的最大生產者數量,預設為10000。 brokerDeduplicationEntriesInterval 重複訊息快照中條目數量,預設為1000。 brokerDeduplicationSnapshotIntervalSeconds 生成訊息快照的間隔週期,預設為120s。 brokerDeduplicationProducerInactivityTimeoutMinutes Broker丟棄不活動生產者快照的等待時間,如果一個生產者在指定時間內不與Broker保持心跳,超過掛職後會刪除對應的快照,單位為分鐘,預設為360,表示6小時。
預設情況下,在所有Pulsar名稱空間/主題上禁用訊息重複刪除。要在所有名稱空間/主題上啟用它,請將brokerDeduplicationEnabled引數設定為true並重新啟動Broker。
如果Broker端將brokerDeduplicationEnabled設定為false,我們也可以pulsar-admin namespaces set-deduplication或者pulsar-admin topics set-deduplication命令在namespace或者主題級別開啟訊息重複刪除。示例如下:
bin/pulsar-admin namespaces set-deduplication public/default --enable
如果要禁用,命令如下:
bin/pulsar-admin namespaces set-deduplication public/default --disable
如果在Pulsar broker、名稱空間或主題中啟用了訊息去重功能,建議客戶端無限次地重試訊息,直到成功為止,否則可能會破壞順序保證,因為一些請求可能會超時,並且應用程式不知道請求是否成功新增到主題。
如果開啟訊息重複刪除機制,建議客戶端配合做如下兩件事情:
為生產者制定一個全域性唯一的名稱 將訊息傳送超時設定為0,表示無超時時間
討論點:訊息傳送端Pulsar提供的SinglePartition負載均衡演算法對應的應用場景是什麼?歡迎留言或微信dingwpmz,一起交流,一起成長。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2939556/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Redis 主從複製詳細解讀Redis
- iOS訂閱詳解iOS
- Robot Framework(12)- 詳細解讀 RF 的變數和常量Framework變數
- SqueezeNet詳細解讀
- mysql主從複製詳細部署MySql
- 主題訂閱推送助力雙十一預約搶購
- Redis持久化、主從與哨兵架構詳解Redis持久化架構
- 【詳解】ThreadPoolExecutor原始碼閱讀(三)thread原始碼
- 【詳解】ThreadPoolExecutor原始碼閱讀(二)thread原始碼
- 【詳解】ThreadPoolExecutor原始碼閱讀(一)thread原始碼
- 詳細解讀go語言中的chnanelGoNaN
- Redis 的訂閱與釋出Redis
- 手寫 Promise 詳細解讀Promise
- 12 個最佳 GNOME(GTK)主題
- 從釋出訂閱模式入手讀懂Node.js的EventEmitter原始碼模式Node.jsMIT原始碼
- Youtube訂閱——解決在彈窗內使用Youtube訂閱按鈕高度顯示不全的問題
- Caddy 原始碼閱讀(一)Run 詳解原始碼
- 詳細解讀微服務的兩種模式微服務模式
- mysql主從複製配置與問題解決MySql
- Oracle SCN機制詳細解讀Oracle
- 矩陣分解--超詳細解讀矩陣
- Android BLE藍芽詳細解讀Android藍芽
- 6個方面教你如何解決 vps主機主機變慢的問題
- 詳解 RxJava 的訊息訂閱和執行緒切換原理RxJava執行緒
- 深入 Redis 主從複製的原理詳解Redis
- 深入詳解Redis 主從複製的原理!Redis
- 詳解Redis主從及哨兵模式Redis模式
- TCP/IP詳解卷二閱讀後記TCP
- TCP/IP詳解卷一閱讀後記TCP
- TCP/IP詳解卷三閱讀後記TCP
- 詳細解讀Service Mesh的資料面Envoy
- C++指標的概念解讀 超詳細C++指標
- Java面試-List中的sort詳細解讀Java面試
- MySQL主從複製中的“show slave status”詳細含義MySql
- TDSQL | 深度解讀HTAP系統的問題與主義之爭SQL
- 基於 Redis 的訂閱與釋出Redis
- 詳細解讀阿里手冊之MySQL阿里MySql
- 手寫 call apply bind 詳細解讀APP