關於 StreamNative
StreamNative 是一家開源基礎軟體公司,由 Apache 軟體基金會頂級專案 Apache Pulsar 創始團隊組建而成,圍繞 Pulsar 打造下一代雲原生批流融合資料平臺。StreamNative 作為 Apache Pulsar 商業化公司,專注於開源生態和社群構建,致力於前沿技術領域的創新,創始團隊成員曾就職於 Yahoo、Twitter、Splunk、EMC 等知名大公司。
導語:本文是 Apache Pulsar PMC 成員,StreamNative 首席架構師李鵬輝在 TGIP-CN 037 直播活動的文字整理版本。Pulsar 2.10.0 版本即將釋出,本場直播為大家帶來 Apache Pulsar 2.10.0 的主要新特性及版本解讀,解答大家對新版本對於技術細節的疑問。
點選 檢視回顧視訊
Pulsar 2.10.0 包含來自於 99 位貢獻者的 1000+ commits,其中諸多貢獻來自於國內的貢獻者,感謝大家對 Pulsar 的支援與貢獻。 本次版本釋出是一次新的里程碑,如此多的 commit 數量也為文件帶來了升級;Apache Pulsar 網站升級中,新網站 Beta 版本對文件進行了重新歸檔與完善,歡迎大家試用並提出寶貴意見。
Apache Pulsar 2.10.0 版本新特性內容包括:
- 去除對 ZooKeeper 的強依賴;
- 新的消費型別 TableView;
- 多叢集自動故障轉移;
- Producer Lazy Loading + Partial RoundRobin;
- Redeliver Backoff;
- Init Subscription for DLQ;
- 引入多叢集全域性 Topic Policy 設定支援以及 Topic 級別的跨地域複製配置;
- ChunkMessageId;
- 增加批量操作 Metadata 服務的支援:可以在大量 Topic 的場景下提升 Pulsar 穩定性;
- ...
去除對 ZooKeeper API 強依賴
ZooKeeper 是 Pulsar 中使用非常廣泛的一個 API,舊版對該 API 的依賴無處不在,但這種依賴不利於使用者選擇其他型別的後設資料服務。為了解決這一問題,Pulsar 經過多個版本的迭代,做了大量準備和測試工作後終於在 2.10.0 版本去除了對 ZooKeeper 的強依賴。
目前新版支援三種後設資料服務:
- ZooKeeper
- Etcd
- RocksDB(standalone)
其中需要注意的是 Etcd 目前沒有很好的 Java 客戶端,綜合考量使用要慎重。此外從 benchmark 測試成績來看 ZooKeeper 與 Etcd 的效能是相近的,使用者可以根據自身情況來選擇。
該特性的提案是 PIP 45 : Pluggable metadata interface(Metadata Store + Coordination Service)。顧名思義,這裡的 Metadata Store 是一個後設資料儲存,而 Coordination Service 則提供了一箇中心化服務來獲得全域性鎖。
2.10 版本還增加了 Metadata 批量操作支援,降低客戶端與服務端互動需求,大幅減輕了 metadata 的操作壓力。
# The metadata store URL
# Examples:
# * zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181
# * my-zk-1:2181,my-zk-2:2181,my-zk-3:2181 (will default to ZooKeeper when the schema is not specified)
# * zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181/my-chroot-path (to add a ZK chroot path)
metadataStoreUrl=
# The metadata store URL for the configuration data. If empty, we fall back to use metadataStoreUrl configurationMetadataStoreUrl=
以上就是新特性的 API 實現,可以看到上述配置引數已經不再需要 ZooKeeper。但去除對 ZooKeeper 的依賴並不代表會將其移除。考慮到 ZooKeeper 還有大量使用者,使用較為廣泛,所以短期內官方並不會考慮刪除實現,而只是將其外掛化來方便使用。
更多減輕 ZooKeeper 依賴細節,可以閱讀部落格 Apache Pulsar 輕裝上陣:邁向輕 ZooKeeper 時代。
新的消費型別 TableView
Pulsar 的消費模式比較多樣化,如今 2.10 版本再引入了 TableView,這是一個類似於 KV 的表格服務。它是一個不支援寫入的純檢視,可以直接在客戶端記憶體構建表格檢視,適合資料量不大的場景生成檢視。但 TableView 不太合適資料量較大、單臺機器的記憶體都難以承受的場景。
TableView 可用於配合 Topic Compaction,後者可以在服務端做 Key 的壓縮。這一特性的原理是隻在一個 snapshot 中儲存 key 的最新狀態,consumer 需要時只需檢視這個 snapshot,而無需去消耗更多成本讀取原始 backlog。TableView 可以與該特性無縫銜接,恢復 TableView 時就可以直接利用 broker 生成的 snapshot,從而減小恢復開銷。原視訊 22 分處具體介紹了這種壓縮的機制和使用場景。
try (TableView<byte[]> tv = client. newTableViewBuilder (Schema.BYTES)
.topic ("public/default/tableview-test")
.autoUpdatePartitionsInterval(60, TimeUnit.SECONDS)
.create()) {
System.out.println("start tv size: " + tv.size());
tv. forEachAndListen((k, v) -> System.out.println(k + "->"+ Arrays. toString(v)));
while (true) f
Thread. sleep (20000) ;
System.out.println(tv.size)):
tv. forEach((k, v) -> System, out.println("checkpoint: "+ k+ "->" + Arrays.toString(v)));
}
} catch (Exception ex) {
System.out.println("Table view failed: " + ex. getCause());
}
Cluster 自動故障轉移
ServiceUrlProvider provider = AutoClusterFailover.builder()
.primary(primary)
.secondary(Collections.singletonList(secondary))
.failoverDelay(failoverDelay, TimeUnit.SECONDS)
.switchBackDelay(switchBackDelay, TimeUnit.SECONDS)
.checkInterval(checkInterval, TimeUnit.MILLISECONDS)
.build();
Pulsar 支援多叢集,叢集間可同步資料,因此使用者經常會有叢集間的故障轉移需求,所以引入自動故障轉移特性。過去做故障轉移時需要通過域名切換,或者使用自制的輔助節點,但這些方法往往都需要人工介入,SLA 難以保障。新特性的優勢在於自動化與可配置,可設定 primary 與 secondary 叢集,配置延遲等引數,通過探活實現叢集自動按預期切換。但目前該特性在探活時只探測 Pulsar 埠是否接通,未來版本將繼續改進探活方式。
Producer Lazy loading + Partial RoundRobin
當前在一個規模較大的叢集中,如果 partition 比較多,則傳送訊息時 producer 需要輪詢所有 partition,且 partition 可能分佈在不同 broker 上可能產生巨大的連線壓力,如上圖上半部分。為此,該新特性實現了 producer 懶載入,這樣一來如果不用某個 partition 就不會建立它,減輕了系統負擔。而部分輪詢會先 List 所有 partition 再將它們 shuffle,實現不同客戶端寫入不同的 partition,減少 producer 例項與 broker 的連線數量,同樣可以降低系統壓力。需要注意的是 Shared consumer 暫時不支援這種機制,未來需要社群共同探索這一方面能否實現類似的機制。
PartitionedProducerImpl<byte[]> producerImpl = (PartitionedProducerImpl<byte[]>) pulsarClient.newProducer()
.topic(topic)
.enableLazyStartPartitionedProducers(true)
.enableBatching(false)
.messageRoutingMode(MessageRoutingMode.CustomPartition)
.messageRouter(new PartialRoundRobinMessageRouterImpl(3))
.create();
Redeliver Backoff
client.newConsumer().negativeAckRedeliveryBackoff(MultiplierRedeliveryBackoff.builder()
.minDelayMs(1000)
.maxDelayMs(60 * 1000)
.build()).subscribe();
client.newConsumer().ackTimeout(10, TimeUnit.SECOND)
.ackTimeoutRedeliveryBackoff(MultiplierRedeliveryBackoff.builder()
.minDelayMs(1000)
.maxDelayMs(60 * 1000)
.build()).subscribe();
Pulsar現有 ackTimeout 機制,如果使用 shared 訂閱,消費資料時可能在一段時間內無法簽收,則 ackTimeout 可以保證超過一定時間仍未簽收時客戶端自動重新投遞訊息,將訊息重新分發到其他 consumer 上。
訊息重新投遞的時間很難確定,長短不一,且隨著訊息處理失敗次數越來越多,延遲需要越來越長。為此引入了該 API,可以逐漸延長延遲。相比現有方法,這個特性的優勢在於開銷更小,不需要經過另一個 topic,且更加靈活。不足之處是一旦客戶端當機會導致訊息立即重試。另外該特性使用成本很低,API 簡潔,很容易掌握。需要注意的是目前只有 Java 客戶端支援該特性,另外它可以配合死信佇列使用。
初始化死信佇列訂閱
Consumer<byte[]> consumer = pulsarClient.newConsumer(Schema.BYTES)
.topic(topic)
.subscriptionName(subscriptionName)
.subscriptionType(SubscriptionType.Shared)
.ackTimeout(1, TimeUnit.SECONDS)
.deadLetterPolicy(DeadLetterPolicy.builder()
.maxRedeliverCount(maxRedeliveryCount)
.initialSubscriptionName(my-sub)
.build())
.receiverQueueSize(100)
.subscriptionInitialPosition(SubscriptionInitialPosition.Earliest )
.subscribe();
死信佇列的建立是懶建立策略,這就出現了一個問題:死信訊息尚未發生、topic 尚未建立時,就無法給 topic 指定資料保留策略。為此只能給 namespace 建立策略,粒度會很大。新版引入 InitialSubscriptionName
,在設定死信佇列時可以在 create 時同時建立一個訂閱,這樣資料就可以保留下來。且對於死信佇列,大部分場景僅需一個訂閱處理即可,於是 訂閱就會和 InitialSubscriptionName
對應,這樣無需設定 retention,就可以保留髮到死信佇列的訊息。
跨叢集 topic 策略
bin/pulsar-admin topics set-retention -s 1G -t 1d --global my-topic
Message message = MessageBuilder.create()
...
.setReplicationClusters(restrictDatacenters)
.build();
producer.send(message);
該特性可以跨叢集應用 topic policy,通過 -global
引數對所有叢集生效。表面上來看一個全域性引數很容易實現,其實背後做了很多工作。主要是底層需要將 schema 同步到所有叢集,才能做到跨叢集應用。需要注意的是 broker 沒有重試策略,以下兩種方式任選其一:
- 主動告知 broker 重試;
- 斷開客戶端。
新的訊息 ID 型別 ChunkMessageId
public class ChunkMessageIdImpl extends MessageIdImpl implements Messaged {
private final MessageIdImpl firstChunkMsgId;
public ChunkMessageIdImpl(MessageIdImplfirstChunkMsgId,MessageIdImpllastChunkMsgId){
super(lastChunkMsgId.getLedgerId(), lastChunkMsgId.getEntryId(), lastChunkMsgId.getPartitionIndex());
this. firstChunkMsgId = firstChunkMsgId;
}
public MessageIdImplgetFirstChunkMessageId(){
return firstChunkMsgId;
}
public MessageIdImplgetLastChunkMessageId(){
return this;
}
}
之前版本引入的 ChunkMessage 可以有效減輕系統壓力,但存在的問題是 Chunk 中只有最後的 MessageId 返回給客戶端,這樣客戶端就無法知曉前面的 Id。這個特性解決了 ChunkMessage 開始到結束對應的 Id 缺失問題,方便使用者 seek MessageId 並消費。目前該特性只有 Java 客戶端支援。
其他特性
- Topic properties:給 Topic 附加 name 之外的更多資訊,與 metadata 一併儲存;
- Metadata batch operation:提升效能;
- CPP client:提供 chunk message 支援;
- Broker graceful shutdown:支援 REST API 優雅地關閉 Broker,先將 Broker 從叢集拿掉後再關閉 Topic,避免繼續發起對 Broker 的連線;
Support creat a consumer in the paused state
:在 create consumer 時可以指定暫停狀態,不向服務端獲取訊息;- ...
精選 Q&A
新特性介紹完畢後,李老師還對觀眾彈幕問題一一作了解答,以下為 QA 精選內容概要,詳情見視訊 54 分鐘後。
Q:Pulsar 是否支援 failover?
- Pulsar 支援 failover 模式,且一個 partition 可以有多個 consumer,開銷主要存在於訊息簽收上。Pulsar 支援維護單條訊息簽收狀態,因此會有一定開銷;
Q:Pulsar 對 ack 的操作是 exactly once 嗎?
- Pulsar 對不開啟 transaction 的情況預設是 at least once 實現,而不是 exactly once;
Q:ChunkMessage 支援事務嗎?
- 目前暫不支援事務;
Q:Pulsar 的訊息傳送是否會有中間失敗後面成功的情況?
- Pulsar 中所有訊息的傳送不會出現中間失敗後面成功的情況,其中一個失敗後面都會失敗;
Q:後設資料導致的叢集規模限制問題如何解決?
- 2.10 版本暫未解決後設資料導致的叢集規模限制問題,未來考慮解決;
Q:KoP 支援 Kafka format 嗎?
- 現在 KoP 可以支援 Pulsar format 與 Kafka format,避免服務端的序列化/反序列化,把工作交給客戶端。客戶端載入一個 formatter 就可以解析 Kafka format 資料,減輕對 broker 的壓力。
關於PPT
請複製連結到瀏覽器下載PPT:https://pan.baidu.com/s/1sqt9...
密碼: 6wtk
相關閱讀
- 直播回顧|TGIP-CN 036:Apache Pulsar 最新技術進展與動態
- 直播回顧| TGIP-CN 035: Apache Pulsar 動手實戰第二期:容器部署實戰
- 博文推薦|Apache Pulsar 輕裝上陣:邁向輕 ZooKeeper 時代`
關注公眾號「Apache Pulsar」,獲取更多技術乾貨
加入 Apache Pulsar 中文交流群??
點選立即觀看 TGIP-CN 37:Apache Pulsar 2.10.0 新特徵解析 回顧視訊!