Pulsar 架構(gòu)的的疑問和改進
本文會討論 Pulsar 整體架構(gòu)的一些疑問和可能的改進,一家之言皆怕。當前的 Pulsar 版本是2.9.1
缅帘,使用的 BookKeeper 和 Zookeeper 版本分別是 4.14.2
和3.6.3
磷杏。
實際上对竣,BookKeeper 和 Zookeeper 也是單獨的 Apache 項目庇楞,Pulsar 項目其實主要就是 Pulsar Broker。為了便于理解否纬,本文用 Broker 代指 Pulsar Broker姐刁,用 Pulsar 代指包括 Broker、BookKeeper 和 Zookeeper 三個模塊在內(nèi)的整個系統(tǒng)烦味。
關(guān)于無狀態(tài)(Stateless)的疑問
其實,Pulsar 的每一個模塊都不是無狀態(tài)的壁拉。
Zookeeper 和 BookKeeper 是無狀態(tài)的谬俄,很好理解。因為弃理,這兩個模塊都存有數(shù)據(jù)溃论,Zookeeper 的各個節(jié)點雖然是有最終一致性保證,但是 Leader痘昌、Follower 等節(jié)點角色的存在钥勋,導(dǎo)致了節(jié)點處理流程的不同,是有狀態(tài)的辆苔;BookKeeper 就更好解釋了算灸,因為 BookKeeper 節(jié)點本地存儲不同 Ledger 的數(shù)據(jù),訪問不同 Ledger 要路由到不同的節(jié)點驻啤,因此也是有狀態(tài)的菲驴。
Broker 本身并不持久化任何數(shù)據(jù),其運行所需要的數(shù)據(jù)都存儲在 BookKeeper 和 Zookeeper 上骑冗。那么我們可以說 Broker 是無狀態(tài)的嗎赊瞬?
仍然不可以,無狀態(tài)的定義最早是來自微服務(wù)領(lǐng)域贼涩,參考 Microsoft Azure 的定義巧涧,
A stateless service is one where there is no state maintained within the service across calls. Any state that is present is entirely disposable and doesn't require synchronization, replication, persistence, or high availability.
指的是,除了服務(wù)調(diào)用帶來的狀態(tài)遥倦,即參數(shù)谤绳,本身沒有任何狀態(tài)。這個定義之后,Azure 還舉例 Calculator 服務(wù)是無狀態(tài)的闷供,還做了一些補充說明烟央,
Not storing any internal state makes this example calculator simple. But most services aren't truly stateless. Instead, they externalize their state to some other store. (For example, any web app that relies on keeping session state in a backing store or cache is not stateless.)
進一步說明,不存儲“內(nèi)部狀態(tài)”的服務(wù)才可以算是無狀態(tài)服務(wù)歪脏,對于一些存儲了“內(nèi)部狀態(tài)”的服務(wù)疑俭,例如,Session 信息婿失,內(nèi)存緩存钞艇,這些都不能算是無狀態(tài)服務(wù)。除非這些內(nèi)部狀態(tài)不存儲在服務(wù)內(nèi)豪硅,而是由其他的“有狀態(tài)”服務(wù)來完成哩照。
那么很顯然,按上面的定義懒浮,Broker 肯定不是無狀態(tài)的飘弧。
首先,Broker 是 Topic 的所有者砚著,所有某個特定 Topic 的訪問次伶,都需要經(jīng)過特定的 Broker 來完成。這就是有狀態(tài)的稽穆,內(nèi)部存儲了 Topic Owner 信息冠王。
還有,Broker 是有緩存的舌镶,內(nèi)存緩存了 Entry 信息柱彻,按上面的定義,這也是有狀態(tài)的餐胀。
再有哟楷,當 Broker 故障的時候,需要有特別的 Fencing 機制來啟動新的 Broker 以提供服務(wù)否灾。而無狀態(tài)服務(wù)是不需要特別的高可用策略的吓蘑,只需要提供服務(wù)節(jié)點即可。
綜上坟冲,Broker 并不是“無狀態(tài)”的磨镶。
當然,中間件的優(yōu)秀與否健提,并不是由是否“無狀態(tài)”來判定的琳猫,厘清這個概念會有助于更好的理解 Pulsar。
關(guān)于多 Ledger 存儲
Pulsar 中有 Topic私痹、Ledger 的概念脐嫂,BookKeeper 中有 Ledger统刮、Fragment、Entry 的概念账千。
對比 Kafka 的設(shè)計侥蒙,Kafka 中只有 Topic、Ledger 的概念匀奏,而且 Topic 和 Ledger 是一對一的鞭衩,而且 Ledger 和物理存儲也是一對一的。也就是說娃善,Kafka 中论衍,同一個 Topic 的存儲是存儲在一起的,放在一個 Ledger 中聚磺。這帶來的問題就是坯台,隨著topic中的數(shù)據(jù)越來越多,Kafka 的存儲會變的很大瘫寝。如果單個節(jié)點發(fā)生異常蜒蕾,恢復(fù)節(jié)點的時候,復(fù)制的數(shù)據(jù)也會非常多焕阿,而且必須要等待數(shù)據(jù)都恢復(fù)完了才可以提供服務(wù)滥搭,影響恢復(fù)時間。如果需要增加 Topic 的副本數(shù)捣鲸,也需要復(fù)制整個 Topic 的數(shù)據(jù),也非常耗時闽坡。通常情況下栽惶,歷史的消息往往是不重要的,我們更關(guān)注的是未來的消息疾嗅,但在 Kafka 的機制下外厂,我們別無選擇,只能復(fù)制全部數(shù)據(jù)代承。
Pulsar 的多 Ledger 存儲機制解決了這個問題汁蝶,是消息中間件的一次成功嘗試。
首先论悴,Pulsar 中的 Topic 和 Ledger 是一對多的掖棉,一個Topic有多個 Ledger。不同的 Ledger 可以存儲在不同的節(jié)點上膀估,每個節(jié)點上也可以存儲多個 Ledger 的數(shù)據(jù)幔亥。不同的 Ledger 也可以有不同的副本數(shù)配置。當發(fā)生節(jié)點故障察纯,需要數(shù)據(jù)恢復(fù)的時候帕棉,恢復(fù)的流程會相對復(fù)雜一點针肥,需要復(fù)制多個 Ledger 的數(shù)據(jù)。雖然需要復(fù)制的數(shù)據(jù)量仍然很大香伴,但是一個一個 Ledger 的復(fù)制慰枕,先復(fù)制完的 Ledger 就可以正常提供服務(wù)了。當然即纲,多個 Ledger 的設(shè)計也會有副作用具帮,當一個 Bookie 節(jié)點故障的時候,可能會影響多個 Topic 提供服務(wù)崇裁,這需要我們在設(shè)計 Ledger 副本數(shù)的時候匕坯,對每個 Ledger 都有災(zāi)備的考慮。
還有拔稳,對于增加副本的場景葛峻,如果不溯及過往的話,在 Pulsar 中是非常簡單的巴比,只要將當前 Topic 新增一個 Ledger术奖,并在新增的 Ledger 中設(shè)置期望的副本數(shù)即可。這個應(yīng)該是 Pulsar 的殺手級應(yīng)用場景轻绞。
再有采记,Bookie 的 Ledger 是混雜存儲的,同一個存儲內(nèi)的數(shù)據(jù)可能來自多個 Ledger政勃,這個存儲的設(shè)計有點類似于 RocketMQ唧龄。Bookie 為了利用磁盤順序讀緩存 Entry,在每次將 Write Cache 寫入磁盤的時候奸远,將 Entry 按 Ledger id 和 Entry id 進行排序既棺,結(jié)果是同一個 Ledger 的數(shù)據(jù)基本上得到了聚合。這個機制非常依賴于 Write Cache 的大小和當前 Bookie 打開的 Ledger 數(shù)量懒叛。如果 Write Cache 太小了丸冕,或者打開的 Ledger 數(shù)量太多了,起到的聚合作用也很弱薛窥。推薦 Write Cache 盡量大一些胖烛。可以做一個估算诅迷,假設(shè)預(yù)讀取(readahead)1000個 Entry佩番,平均每個 Entry 大小5KB,一次預(yù)讀取罢杉,大約需要讀5MB答捕。假設(shè) Bookie 打開了5個 Ledger,同時屑那,消息的大小拱镐,產(chǎn)生消息的速度艘款,和預(yù)讀取的參數(shù)也都基本一致,那么沃琅,理想情況下哗咆,Write Cache 需要至少有5MB*5=25MB,預(yù)讀取才會比較有效益眉,假設(shè)預(yù)讀取的起始 Cursor 位置是平均分布的晌柬,那么每次預(yù)讀的數(shù)量大概在500條。如果增大 Write Cache 的大小到250MB(10倍)郭脂,同樣進行上面的計算年碘,得到每次預(yù)錄取的消息數(shù)量是950條。這就很接近于我們預(yù)設(shè)的預(yù)讀取參數(shù)了展鸡,而且預(yù)讀取消息數(shù)量的方差也比原先要小很多屿衅,會降低很多系統(tǒng)性能的抖動。實際上莹弊,可以對 Entry 進行重新整理涤久,使 Entry 進一步聚合(但不一定真的需要)。這個可以在 Pulsar 做GC忍弛,或者分層存儲响迂,把數(shù)據(jù)刷到長期存儲的時候?qū)崿F(xiàn)。
此外细疚,BookKeeper 的存儲把 Journal 和 Entry Log 分開了蔗彤,可以存儲在不同的磁盤上,這個相對于 Kafka 的確是一個進步疯兼。因為這兩者的存儲特性是不一樣的然遏。Journal 要求的是事務(wù)性,速度快镇防,不要求大容量;Entry Log 要求的是讀取高效潮饱,寫入可以慢来氧,容量要大。分開存儲可以利用好不同硬件設(shè)備的硬件特性香拉。
關(guān)于共識機制和存儲的分離
Pulsar 的另一個顯著的特性是共識機制(Consensus)和存儲的分離啦扬。例如,BookKeeper 在整個系統(tǒng)中只是一個單純的存儲功能凫碌,每個 Bookie 節(jié)點的功能是相同的扑毡,不會有角色的差異,雖然存儲的數(shù)據(jù)有不同盛险。Leader 的身份由 Broker 來擔當瞄摊,而 Broker 又不存儲數(shù)據(jù)勋又,于是就達到了共識機制和存儲相分離的架構(gòu)。這種架構(gòu)有個好處换帜,可以比較容易的變更集群的節(jié)點成員楔壤,例如增加存儲節(jié)點,也就是動態(tài)成員管理功能惯驼。當然蹲嚣,由于分離會帶來一些代碼實現(xiàn)上的復(fù)雜性,這個Pulsar 和 BookKeeper 已經(jīng)克服祟牲。
那么隙畜,共識機制和存儲的分離,是動態(tài)成員管理的先決條件嗎说贝?動態(tài)成員管理是一個很好的特性议惰,但我們可以不要共識機制和存儲分離,只要動態(tài)成員管理嗎狂丝?
類比 Kafka 的設(shè)計换淆,Kafka Broker 節(jié)點有 Leader 和 Follower 的區(qū)別粒没,數(shù)據(jù)同步的時候柴底,由 Leader 流向 Follower。但 Kafka 是靜態(tài)成員管理绑蔫,Kafka Leader 故障的時候蛋哭,需要選舉出新的 Leader县习;如果有新的節(jié)點加入,也需要復(fù)制全部的數(shù)據(jù)谆趾。如此這般的特性和 Raft 非常類似躁愿,Raft 的成員管理就非常麻煩,設(shè)計了一整套機制(joint consensus)來應(yīng)對成員變更的場景沪蓬。
誠然彤钟,每一個分布式算法中,成員變更的場景都是非常復(fù)雜的跷叉,甚至有些分布式算法還不能很好支持成員變更的場景逸雹,Raft 的 Joint Consensus 方法來處理成員變更,已經(jīng)是相對簡單云挟,但其復(fù)雜程度也是無法一眼就看明白的梆砸。Pulsar 的動態(tài)成員管理非常的簡易,是如何跳出這么復(fù)雜的算法陷阱的园欣?
實際上帖世,Pulsar 并沒有什么獨到的辦法,只是將增加數(shù)據(jù)節(jié)點(Bookie)沸枯,解釋成了成員管理而已日矫。Pulsar 的動態(tài)成員管理赂弓,并不是分布式算法中的分布式集群成員管理。Pulsar 的共識機制的來源是 Zookeeper(使用 ZAB 分布式協(xié)議搬男,與 Raft 相似)拣展,其增加的 Bookie 節(jié)點,并不是 Zookeeper 的成員缔逛,而只是數(shù)據(jù)存儲的一個地址而已备埃。如果增加了一個 Bookie 節(jié)點,只需要在 Zookeeper 中增加該節(jié)點的地址褐奴,于是按脚,復(fù)雜的分布式集群成員管理問題,被取代為分布式集群中增加一條數(shù)據(jù)的問題敦冬,問題的復(fù)雜度被大大降低了辅搬。
同樣的道理,Pulsar 的 Broker 節(jié)點也可以增加脖旱,利用 Zookeeper 增加多個備選的 Broker堪遂。當 Leader Broker 故障的時候,選取新的 Leader 也不是分布式算法中選舉 Leader 的過程萌庆,而是多個 Broker 去“搶占”某個 Topic 的所有權(quán)溶褪。整個過程類似于競爭分布式鎖,哪個 Broker 獲得了這個鎖践险,就相當于持有了 Topic 的所有權(quán)猿妈,成為了 Leader。選舉問題也被大大簡化了巍虫。
想明白了 Pulsar 動態(tài)成員管理功能的實現(xiàn)彭则,就可以類比 Raft 和 Kafka 了。Raft 是一個分布式算法占遥,Raft 集群如果需要成員變更就需要按算法的機制進行俯抖,即使復(fù)雜也沒辦法。Kafka 不是一個分布式算法瓦胎,其共識機制的來源是 Zookeeper芬萍,Kafka Broker 集群如果要變更 Broker 節(jié)點,也是向 Zookeeper 寫入數(shù)據(jù)凛捏;Kafka Broker 如果要重新選取 Leader担忧,也是要去 Zookeeper 中搶占一個鎖芹缔。所以 Kafka 共識機制的實現(xiàn)原理和 Pulsar 是一致的坯癣,那么,為什么 Kafka 沒有動態(tài)成員管理呢最欠?
如果按照 Pulsar 對成員管理的解釋示罗,Kafka 也是有動態(tài)成員管理功能的惩猫,但是這個功能實在有些糟糕。比如蚜点,加入一個節(jié)點轧房,需要復(fù)制 Topic 所有已知歷史的數(shù)據(jù),性能上并不可觀绍绘,這樣的功能無法作為一個功能點來宣傳奶镶,
反觀 Pulsar,加入節(jié)點不需要溯及歷史陪拘,也沒有數(shù)據(jù)復(fù)制厂镇,加入之后只需要等待 Topic 下一次創(chuàng)建 Ledger 的時候,選到新的 Bookie 即可左刽;即使成員的數(shù)量有變化捺信,例如從原先 3 個 Bookie 節(jié)點,變?yōu)?5 個 Bookie 節(jié)點欠痴,也就是在創(chuàng)建 Ledger 的時候多選幾個節(jié)點迄靠,代碼的參數(shù)發(fā)生了變化而已。
因此喇辽,Pulsar 動態(tài)成員管理的功能掌挚,并不是來自于共識機制與存儲的分離,在 Pulsar 的不溯及歷史茵臭、單 Topic 多 Ledger 等特性的前提下疫诽,動態(tài)成員管理是一個非常“自然”能想到的功能旦委。Kafka 是吃了需要復(fù)制歷史數(shù)據(jù)的虧奇徒。
Pulsar 的這種分離架構(gòu)也不是盡善盡美,這個設(shè)計導(dǎo)致了 Pulsar 過度依賴 Zookeeper缨硝,在 Zookeeper 中存儲了太多元數(shù)據(jù)信息摩钙,如果 Zookeeper 故障不可用,Pulsar 集群將幾乎完全宕機查辩。而 Kafka 在 Zookeeper 中的元數(shù)據(jù)較少胖笛,在 Zookeeper 宕機的情況下,依然可以保持基本的服務(wù)宜岛。當然长踊,綜合來看,Pulsar 的這個設(shè)計還是利大于弊的萍倡。
關(guān)于腦裂問題
Pulsar 為了解決 Leader Broker 節(jié)點故障切換的問題身弊,使用了一個叫 Fencing 的機制,解決了 Leader 節(jié)點故障時候的腦裂問題。
方案本身不詳細描述了阱佛,F(xiàn)encing 方案和 Raft Leader 的故障恢復(fù)機制實際上是沒有什么差別的帖汞,應(yīng)該是有所借鑒。至于解決了腦裂凑术,這個也不是真正解決翩蘸,也是由于消息系統(tǒng)的特性導(dǎo)致的直接結(jié)果。為什么這么說呢淮逊?
Raft 中也有類似的概念催首,叫 committed index(Pulsar與之對應(yīng)的是LAC),只有在收到多數(shù)節(jié)點寫入 Entry 返回成功之后泄鹏,才可以更新 committed index翅帜,再更新 Entry 到狀態(tài)機中,并返回給客戶端命满。對尚未更新 committed index 的 Entry涝滴,Raft 也是不可讀的〗禾ǎ可以發(fā)現(xiàn)歼疮,Pulsar 的 Fencing 和 Raft 的機制幾乎一致,但是 Raft 有腦裂問題诈唬。
先回顧一下 Raft 的腦裂問題韩脏。當 Raft Leader 節(jié)點故障發(fā)生時,例如 Raft Leader 網(wǎng)絡(luò)斷開铸磅,其他節(jié)點已經(jīng)發(fā)現(xiàn)當前 Leader 超時赡矢,并發(fā)起下一輪選舉投票,快速選舉出新的 Leader阅仔,但是老 Raft Leader 的 Follower 無響應(yīng)超時時間尚未到達吹散,導(dǎo)致老 Leader 仍然認為自己是真正的 Leader,并響應(yīng)客戶端的請求八酒,因此導(dǎo)致客戶端讀取到了舊的數(shù)據(jù)空民。而與此同時,部分客戶端連接到了新 Raft Leader羞迷,寫入并讀取到新的數(shù)據(jù)界轩,造成不一致,這是 Raft 發(fā)生腦裂的原因衔瓮。Raft 發(fā)生腦裂不會持續(xù)很長時間浊猾,當老 Leader 發(fā)現(xiàn)長時間沒有收到 Follower 響應(yīng)而超時(主要取決于超時參數(shù)的配置),或者發(fā)現(xiàn)有新 Leader 產(chǎn)生時热鞍,老 Leader 就會將自己重置為 Follower葫慎。
那使用了同樣機制的 Pulsar 為什么就沒有腦裂問題呢单山?那是因為,Pulsar 是個消息系統(tǒng)幅疼,寫入的消息類似 WAL,是不可變的(immutable)昼接,追加的爽篷。當發(fā)生和 Raft 一樣的故障的時候,老的 Pulsar Broker 也會讀到老的數(shù)據(jù)慢睡,但老的數(shù)據(jù)仍然合法逐工,因為對同樣的 Cursor,在新的 Broker 上也是讀到相同的數(shù)據(jù)漂辐,只要讀取 Entry 不超過 LAC 就沒問題泪喊,最多只是無法獲取到最新的消息而已,獲取的消息并不會錯髓涯。而 Raft 的存儲是偏向于通用存儲場景袒啼,因此就會有新舊數(shù)據(jù)版本不一致的問題。
腦裂一般都是指讀取數(shù)據(jù)發(fā)生的不一致纬纪,如果是寫入數(shù)據(jù)的腦裂蚓再,那可能是分布式算法有問題,成熟的算法一般不會有這個問題包各。
關(guān)于和Kafka的性能對比
老實說摘仅,目前的性能測試報告都顯示,Pulsar 的性能高于 Kafka 很多问畅,其實有些費解娃属。尤其是簡化的測試場景,比如單個 Topic护姆,副本3個矾端,寫入集2個,這樣的場景說實話卵皂,Kafka 的實現(xiàn)和 Pulsar 幾乎就是一樣的须床,為什么會有如此大的性能差異呢?
我們假定性能差異確實是存在的(很遺憾我手邊沒有環(huán)境做這樣的性能測試)渐裂,Pulsar 的性能高出 Kafka 一截豺旬,這樣的性能差異是如何造成的呢?
我們先排除代碼實現(xiàn)層面的差異柒凉,兩個中間件的開發(fā)者應(yīng)該都是相當資深的工程師族阅,本身代碼寫法引起的性能差異應(yīng)該并不顯著,也太過于細節(jié)膝捞。
首先坦刀,比較一下簡單場景的測試場景是否公平。單個 Topic,副本3個鲤遥,寫入集2個沐寺,一邊寫入,另一邊消費盖奈,這個場景應(yīng)該沒有任何問題混坞,也是我們絕大多數(shù)情況下遇到的場景。由于幾乎在寫入完成后的同時钢坦,就會有消息消費究孕,因此,這些消息都會有內(nèi)存緩存爹凹。簡單來說厨诸,這個場景就是一邊磁盤寫,一邊內(nèi)存讀的場景禾酱。
測試場景沒有問題微酬,就需要細節(jié)分析兩者處理流程的差異了。先分析一下寫入的性能颤陶,Pulsar 的寫入是先到 Broker得封,再到 Bookie,然后返回指郁;Kafka 的寫入是先到 Leader Broker忙上,再同步到 insync 的 Broker,然后返回闲坎。從網(wǎng)絡(luò)調(diào)用的路徑上來說疫粥,兩者都有一層網(wǎng)絡(luò)調(diào)用。在寫入磁盤的時候腰懂,兩者都是以追加寫的方式把消息寫入文件梗逮,如果寫入的存儲沒有硬件上的差異,那么這一塊應(yīng)該也沒有什么差異绣溜】锻總之,并不覺得寫入的部分造成了性能的差異怖喻。
再看一下寫入的性能差異底哗。其實,兩者都是內(nèi)存讀锚沸,性能差異應(yīng)該很小跋选,實現(xiàn)層面上來看,Pulsar 的內(nèi)存緩存是自己實現(xiàn)的哗蜈,Broker 和 BookKeeper 有2層緩存前标,這個場景下坠韩,基本都會命中 Broker 緩存,也就是第一層緩存炼列;Kafka 的內(nèi)存緩存是借用的 Linux Page Cache 機制只搁,相當于是操作系統(tǒng)提供的,只有1層緩存俭尖,在這個場景下也基本都能命中氢惋。如果說,讀取的性能有差異目溉,那么就是內(nèi)存緩存和 Page Cache 的差異,畢竟 Page Cache 是內(nèi)核態(tài)的東西菱农,系統(tǒng)調(diào)用的確是有損耗的缭付。但 Kafka 用了零拷貝的 sendfile()
系統(tǒng)調(diào)用,應(yīng)該也賺回來了一次系統(tǒng)調(diào)用的消耗循未,Pulsar 在發(fā)送的時候陷猫,需要調(diào)用 Socket 的 send()
將緩存的數(shù)據(jù)發(fā)送除去,也有一次內(nèi)存從用戶態(tài)到內(nèi)核態(tài)的消耗的妖。如果說這里真的有差異的話绣檬,也就是說,“內(nèi)存緩存+send()
”勝過了“Page Cache+sendfile()
”嫂粟,硬要找個理由的話娇未,Pulsar 的實現(xiàn)只需要1次系統(tǒng)調(diào)用,而 Kafka 需要2次(雖然有一次是零拷貝)星虹。
以上差異零抬,不足以構(gòu)成顯著的性能差異,最多5%以內(nèi)的性能差別宽涌。
但有一個場景平夜,也是比較常見的場景,Pulsar 是遠勝于 Kafka 的卸亮,就是追趕讀(catch-up read)場景忽妒。Pulsar 的 BookKeeper 緩存管理是分為 Write Cache 和 Read Cache 的,寫入數(shù)據(jù)與讀取數(shù)據(jù)的緩存分開管理兼贸,Write Cache 用于追尾讀(tail read)場景段直,Read Cache 用于追趕讀場景,互不干擾溶诞。而 Kafka 的緩存是操作系統(tǒng) Page Cache 提供的坷牛,在追趕讀場景,會擦除掉寫入數(shù)據(jù)的緩存很澄,兩者在競爭 Page Cache 的使用京闰,導(dǎo)致了很多加載到 Cache 的數(shù)據(jù)被擦除后颜及,反復(fù)加載,增大 Cache Mis蹂楣。尤其是追尾讀和追趕讀同時存在的時候俏站,例如比例是一半一半的時候,Kafka 的緩存競爭會更加激烈痊土,性能會下降比較厲害肄扎。
關(guān)于緩存的機制
BookKeeper 的緩存分為 Write Cache 和 Read Cache,但 Broker 的緩存設(shè)計卻不一樣赁酝,沒有做這樣的區(qū)分犯祠。這樣的設(shè)計是非常難以理解的,同樣的技術(shù)場景為什么要采用兩種不同的緩存設(shè)計策略酌呆?
BookKeeper 的讀寫緩存分離衡载,是為了優(yōu)化追趕讀和追尾讀共存的場景,減少對緩存的競爭隙袁,以達到更高的 Cache Hit痰娱。對 Broker 來說,這個場景依然存在菩收,是完全一樣的梨睁,如果 Broker 沒有采用讀寫緩存分離,也會有緩存競爭娜饵,會有很多的請求落到 BookKeeper 上坡贺,白白多了一次網(wǎng)絡(luò)IO的消耗。
另外箱舞,Bookie 上有多個 Ledger 的數(shù)據(jù)拴念,緩存要按 Ledger 分多份,Broker 是單個 Topic 的 Owner褐缠,如果按 Topic 維度來進行緩存政鼠,應(yīng)該也不會涉及到內(nèi)存的過多消耗。
因此队魏,我覺得 Pulsar Broker 也應(yīng)該采用 Write Cache 和 Read Cache 分離的機制公般,以進一步提升性能。
改進點1:集成 Broker 和 BookKeeper
因為共識機制和存儲的分離并不會帶來明顯的收益胡桨,Pulsar 的動態(tài)成員管理是來自于單 Topic 多 Ledger 特性的自然推論官帘,所以,把 Broker 和 BookKeeper 分離成兩個服務(wù)昧谊,恐怕不是一個好的選擇刽虹。如果將共識機制和存儲重新合并在同一個節(jié)點,稱為新 Bookie 節(jié)點呢诬,性能將勢必提升涌哲。
首先胖缤,之前 Pulsar 中需要直接訪問 BookKeeper 的場景將少去一次網(wǎng)絡(luò)調(diào)用,也就是 Broker 在 Cache Mis 的時候阀圾,也不會有網(wǎng)絡(luò)調(diào)用的消耗哪廓,這樣明顯會提升性能。這樣的設(shè)計帶來了另一個直覺的結(jié)果初烘,Leader 節(jié)點上有當前 Topic 的最新 Entry涡真,因此,Leader 的 Write Cache 數(shù)據(jù)就是最新的肾筐,可以很快響應(yīng)追尾讀哆料。
其次,功能方面吗铐,并沒有減少原先的功能东亦,尤其在動態(tài)成員管理這里。如果是非 Leader 節(jié)點故障抓歼,那么恢復(fù)方案和原先一樣讥此,新增 Bookie 加入到成員中拢锹,進行成員更新谣妻,并數(shù)據(jù)復(fù)制;如果是 Leader 節(jié)點發(fā)生故障卒稳,那么恢復(fù)方案會相對復(fù)雜一點蹋半,可以分幾步來描述。如下充坑,
- 在剩下的
E-1
(E 表示集群數(shù)量)個 Bookie 節(jié)點發(fā)起選舉减江,產(chǎn)生新的 Leader; - 新的 Leader 先同步存活的各個節(jié)點的數(shù)據(jù)到 Commit Index捻爷,此時的場景和非 Leader 節(jié)點故障的情況就是一樣的了辈灼;
- 新增 Bookie 加入到成員中,進行成員更新也榄,并數(shù)據(jù)復(fù)制
在選舉 Leader 和同步數(shù)據(jù)到 Commit Index 的時候巡莹,細節(jié)上仍然可以采用類似 Fencing 的機制。這樣也不會減少原有的 Pulsar 靈活的特性甜紫。
還有降宅,Pulsar 原先的設(shè)計造成了,對于單個 Topic 來說 Broker 就是實際上的單點囚霸,擴展性受限腰根。所有的寫入讀取,都需要通過單點的 Broker 來完成拓型,造成系統(tǒng)整體壓力的不平均额嘿。當新 Bookie 同時有 Broker 的功能的時候瘸恼,還可以分散單點 Leader 的壓力,進行另一項優(yōu)化岩睁,“追趕讀優(yōu)化”钞脂。
改進點2:追趕讀優(yōu)化
當共識機制和存儲重新結(jié)合,成為新 Bookie 節(jié)點的時候捕儒,可以引入新的追趕讀優(yōu)化冰啃,設(shè)計原則如下,
- 所有的追趕讀由非 Leader 節(jié)點完成刘莹,Leader 節(jié)點要提示客戶端可供訪問的非 Leader 節(jié)點列表阎毅;
- 所有節(jié)點增加限流機制,Leader 節(jié)點達到限流的時候(指追尾讀達到上限)点弯,提示客戶端訪問非 Leader 節(jié)點扇调;
- 非 Leader 節(jié)點也達到限流的時候,拒絕訪問抢肛;
在這樣的設(shè)計下狼钮,再加上 Write Cache 和 Read Cache 分離的機制,Leader 節(jié)點的壓力被平分到了非 Leader 節(jié)點上捡絮,從根本上解決原有的 Broker 單點的問題熬芜,充分發(fā)揮了集群中每個節(jié)點的處理能力。