Pulsar:下一代消息引擎真的這么強(qiáng)嗎?

背景
我們最近在做新業(yè)務(wù)的技術(shù)選型,其中涉及到了對(duì)消息中間件的選擇;結(jié)合我們的實(shí)際情況希望它能滿足以下幾個(gè)要求:
友好的云原生支持:因?yàn)楝F(xiàn)在的主力語(yǔ)言是 Go,同時(shí)在運(yùn)維上能夠足夠簡(jiǎn)單。官方支持多種語(yǔ)言的 SDK:還有一些Python、Java相關(guān)的代碼需要維護(hù)。最好是有一些方便好用的特性,比如:延時(shí)消息、死信隊(duì)列、多租戶等。
當(dāng)然還有一些水平擴(kuò)容、吞吐量、低延遲這些特性就不用多說(shuō)了,幾乎所有成熟的消息中間件都能滿足這些要求。
基于以上的篩選條件,Pulsar 進(jìn)入了我們的視野。
作為 Apache 下的頂級(jí)項(xiàng)目,以上特性都能很好的支持。
下面我們來(lái)它有什么過(guò)人之處。
架構(gòu)

從官方的架構(gòu)圖中可以看出 Pulsar 主要有以下組件組成:
Broker無(wú)狀態(tài)組件,可以水平擴(kuò)展,主要用于生產(chǎn)者、消費(fèi)者連接;與 Kafka 的 broker 類似,但沒(méi)有數(shù)據(jù)存儲(chǔ)功能,因此擴(kuò)展更加輕松。BookKeeper集群:主要用于數(shù)據(jù)的持久化存儲(chǔ)。Zookeeper用于存儲(chǔ)broker與BookKeeper的元數(shù)據(jù)。
整體一看似乎比 Kafka 所依賴的組件還多,這樣確實(shí)會(huì)提供系統(tǒng)的復(fù)雜性;但同樣的好處也很明顯。
Pulsar 的存儲(chǔ)于計(jì)算是分離的,當(dāng)需要擴(kuò)容時(shí)會(huì)非常簡(jiǎn)單,直接新增 broker 即可,沒(méi)有其他的心智負(fù)擔(dān)。
當(dāng)存儲(chǔ)成為瓶頸時(shí)也只需要擴(kuò)容 BookKeeper,不需要人為的做重平衡,BookKeeper 會(huì)自動(dòng)負(fù)載。
同樣的操作,Kafka 就要復(fù)雜的多了。
特性
多租戶
多租戶也是一個(gè)剛需功能,可以在同一個(gè)集群中對(duì)不同業(yè)務(wù)、團(tuán)隊(duì)的數(shù)據(jù)進(jìn)行隔離。
persistent://core/order/create-order
以這個(gè) topic 名稱為例,在 core 這個(gè)租戶下有一個(gè) order 的 namespace,最終才是 create-order 的 topic 名稱。
在實(shí)際使用中租戶一般是按照業(yè)務(wù)團(tuán)隊(duì)進(jìn)行劃分,namespace 則是當(dāng)前團(tuán)隊(duì)下的不同業(yè)務(wù);這樣便可以很清晰的對(duì) topic 進(jìn)行管理。
通常有對(duì)比才會(huì)有傷害,在沒(méi)有多租戶的消息中間件中是如何處理這類問(wèn)題的呢:
干脆不分這么細(xì),所有業(yè)務(wù)線混著用,當(dāng)團(tuán)隊(duì)較小時(shí)可能問(wèn)題不大;一旦業(yè)務(wù)增加,管理起來(lái)會(huì)非常麻煩。 自己在 topic 之前做一層抽象,但其實(shí)本質(zhì)上也是在實(shí)現(xiàn)多租戶。 各個(gè)業(yè)務(wù)團(tuán)隊(duì)各自維護(hù)自己的集群,這樣當(dāng)然也能解決問(wèn)題,但運(yùn)維復(fù)雜度自然也就提高了。
以上就很直觀的看出多租戶的重要性了。
Function 函數(shù)計(jì)算
Pulsar 還支持輕量級(jí)的函數(shù)計(jì)算,例如需要對(duì)某些消息進(jìn)行數(shù)據(jù)清洗、轉(zhuǎn)換,然后再發(fā)布到另一個(gè) topic 中。
這類需求就可以編寫一個(gè)簡(jiǎn)單的函數(shù),Pulsar 提供了 SDK 可以方便的對(duì)數(shù)據(jù)進(jìn)行處理,最后使用官方工具發(fā)布到 broker 中。
在這之前這類簡(jiǎn)單的需求可能也需要自己處理流處理引擎。
應(yīng)用
除此之外的上層應(yīng)用,比如生產(chǎn)者、消費(fèi)者這類概念與使用大家都差不多。
比如 Pulsar 支持四種消費(fèi)模式:
Exclusive:獨(dú)占模式,同時(shí)只有一個(gè)消費(fèi)者可以啟動(dòng)并消費(fèi)數(shù)據(jù);通過(guò)SubscriptionName標(biāo)明是同一個(gè)消費(fèi)者),適用范圍較小。Failover故障轉(zhuǎn)移模式:在獨(dú)占模式基礎(chǔ)之上可以同時(shí)啟動(dòng)多個(gè)consumer,一旦一個(gè)consumer掛掉之后其余的可以快速頂上,但也只有一個(gè)consumer可以消費(fèi);部分場(chǎng)景可用。Shared共享模式:可以有 N 個(gè)消費(fèi)者同時(shí)運(yùn)行,消息按照round-robin輪詢投遞到每個(gè)consumer中;當(dāng)某個(gè)consumer宕機(jī)沒(méi)有ack時(shí),該消息將會(huì)被投遞給其他消費(fèi)者。這種消費(fèi)模式可以提高消費(fèi)能力,但消息無(wú)法做到有序。KeyShared共享模式:基于共享模式;相當(dāng)于對(duì)同一個(gè)topic中的消息進(jìn)行分組,同一分組內(nèi)的消息只能被同一個(gè)消費(fèi)者有序消費(fèi)。
第三種共享消費(fèi)模式應(yīng)該是使用最多的,當(dāng)對(duì)消息有順序要求時(shí)可以使用 KeyShared 模式。
SDK

官方支持的 SDK 非常豐富;我也在官方的 SDK 的基礎(chǔ)之上封裝了一個(gè)內(nèi)部使用的 SDK。
因?yàn)槲覀兪褂昧?dig 這樣的輕量級(jí)依賴注入庫(kù),所以使用起來(lái)大概是這個(gè)樣子:
SetUpPulsar(lookupURL)
container := dig.New()
container.Provide(func() ConsumerConfigInstance {
return NewConsumer(&pulsar.ConsumerOptions{
Topic: "persistent://core/order/create-order",
SubscriptionName: "order-sub",
Type: pulsar.Shared,
Name: "consumer01",
}, ConsumerOrder)
})
container.Provide(func() ConsumerConfigInstance {
return NewConsumer(&pulsar.ConsumerOptions{
Topic: "persistent://core/order/update-order",
SubscriptionName: "order-sub",
Type: pulsar.Shared,
Name: "consumer02",
}, ConsumerInvoice)
})
container.Invoke(StartConsumer)
其中的兩個(gè) container.Provide() 函數(shù)用于注入 consumer 對(duì)象。
container.Invoke(StartConsumer) 會(huì)從容器中取出所有的 consumer 對(duì)象,同時(shí)開(kāi)始消費(fèi)。
這時(shí)以我有限的 Go 開(kāi)發(fā)經(jīng)驗(yàn)也在思考一個(gè)問(wèn)題,在 Go 中是否需要依賴注入?
先來(lái)看看使用 Dig 這類庫(kù)所帶來(lái)的好處:
對(duì)象交由容器管理,很方便的實(shí)現(xiàn)單例。 當(dāng)各個(gè)對(duì)象之前依賴關(guān)系復(fù)雜時(shí),可以減少許多創(chuàng)建、獲取對(duì)象的代碼,依賴關(guān)系更清晰。
同樣的壞處也有:
跟蹤閱讀代碼時(shí)沒(méi)有那么直觀,不能一眼看出某個(gè)依賴對(duì)象是如何創(chuàng)建的。 與 Go 所推崇的簡(jiǎn)潔之道不符。
對(duì)于使用過(guò) Spring 的 Java 開(kāi)發(fā)者來(lái)說(shuō)肯定直呼真香,畢竟還是熟悉的味道;但對(duì)于完全沒(méi)有接觸過(guò)類似需求的 Gopher 來(lái)說(shuō)貌似也不是剛需。
目前市面上各式各樣的 Go 依賴注入庫(kù)層出不窮,也不乏許多大廠出品,可見(jiàn)還是很有市場(chǎng)的。
我相信有很多 Gopher 非常反感將 Java 中的一些復(fù)雜概念引入到 Go,但我覺(jué)得依賴注入本身是不受語(yǔ)言限制,各種語(yǔ)言也都有自己的實(shí)現(xiàn),只是 Java 中的 Spring 不僅僅只是一個(gè)依賴注入框架,還有許多復(fù)雜功能,讓許多開(kāi)發(fā)者望而生畏。
如果只是依賴注入這個(gè)細(xì)分需求,實(shí)現(xiàn)起來(lái)并不復(fù)雜,并不會(huì)給帶來(lái)太多復(fù)雜度。如果花時(shí)間去看源碼,在理解概念的基礎(chǔ)上很快就能掌握。
回到 SDK 本身來(lái)說(shuō),Go 的 SDK 現(xiàn)階段要比 Java 版本的功能少(準(zhǔn)確來(lái)說(shuō)只有 Java 版的功能最豐富),但核心的都有了,并不影響日常使用。
總結(jié)
本文介紹了 Pulsar 的一些基本概念與優(yōu)點(diǎn),同時(shí)順便討論一下 Go 的依賴注入;如果大家和我們一樣在做技術(shù)選型,不妨考慮一下 Pulsar。
后續(xù)會(huì)繼續(xù)分享 Pulsar 的相關(guān)內(nèi)容,有相關(guān)經(jīng)驗(yàn)的朋友也可以在評(píng)論區(qū)留下自己的見(jiàn)解。
你的點(diǎn)贊與分享是對(duì)我最大的支持

更多推薦內(nèi)容
↓↓↓
《如何參與一個(gè)頂級(jí)的開(kāi)源項(xiàng)目》
《What?一個(gè) Dubbo 服務(wù)啟動(dòng)要兩個(gè)小時(shí)!》
《又一次生產(chǎn) CPU 高負(fù)載的排查實(shí)踐》
《利用策略模式優(yōu)化過(guò)多 if else》
《為自己搭建一個(gè)分布式 IM(即時(shí)通訊) 系統(tǒng)》
《一次生產(chǎn) CPU 100% 排查優(yōu)化實(shí)踐》
《判斷一個(gè)元素在億級(jí)數(shù)據(jù)中是否不存在》
《設(shè)計(jì)一個(gè)可插拔的 IOC 容器》
《一次 HashSet 所引起的并發(fā)問(wèn)題》
