數(shù)據(jù)持久化
一個(gè)Kafka分區(qū)中的所有數(shù)據(jù)都保存在一個(gè)代理服務(wù)器上(并被復(fù)制到別的代理服務(wù)器上)。在配置的有效期過(guò)后數(shù)據(jù)會(huì)失效并被刪除。另外,也可以配置策略讓Kafka的分區(qū)保留每個(gè)主鍵的最新值。
與Kafka相似,DistributedLog也可以為每個(gè)流配置有效期,并在超時(shí)之后將相應(yīng)的日志分片失效或刪除。除此之外,DistributedLog還提供了顯示的截?cái)鄼C(jī)制。應(yīng)用程序可以顯式地將一個(gè)日志流截?cái)嗟搅鞯哪硞€(gè)指定位置。這對(duì)于構(gòu)建可復(fù)制的狀態(tài)機(jī)非常有用,因?yàn)榭蓮?fù)制的狀態(tài)機(jī)需要在刪除日志記錄之前先將狀態(tài)持久化。 Manhattan 就是一個(gè)用到了這個(gè)功能的典型系統(tǒng)。
操作
數(shù)據(jù)分片和分布機(jī)制的不同也導(dǎo)致了維護(hù)集群操作上的不同,擴(kuò)展集群操作就是一個(gè)例子。
擴(kuò)展Kafka集群時(shí),通?,F(xiàn)有分區(qū)都要做重新分布。重新分布操作會(huì)將Kafka分區(qū)挪動(dòng)到不同的副本上,以此達(dá)到均衡分布。這就要把整個(gè)流的數(shù)據(jù)從一個(gè)副本拷到另一個(gè)副本上。我們也說(shuō)過(guò)很多次了,執(zhí)行重新分布操作時(shí)必須非常小心,避免耗盡磁盤(pán)和網(wǎng)絡(luò)資源。
而擴(kuò)展DistributedLog集群的工作方式則截然不同。DistributedLog包含兩層:存儲(chǔ)層(Apache BooKeeper)和服務(wù)層(寫(xiě)入和讀出代理)。在擴(kuò)展存儲(chǔ)層時(shí),我們只需要添加更多的Bookie就好了。新的Bookie馬上會(huì)被寫(xiě)入代理發(fā)現(xiàn),并立刻用于寫(xiě)入新的日志分片。在擴(kuò)展數(shù)據(jù)存儲(chǔ)層時(shí)不會(huì)有任何的重新分布操作。只在增加服務(wù)層時(shí)會(huì)有重新分布操作,但這個(gè)重新分布也只是移動(dòng)日志流的屬主權(quán),以使網(wǎng)絡(luò)代寬可以在各個(gè)代理之間均衡分布。這個(gè)重新分布的過(guò)程只與屬主權(quán)相關(guān),沒(méi)有數(shù)據(jù)遷移操作。這種存儲(chǔ)層和服務(wù)層的隔離不僅僅是讓系統(tǒng)具備了自動(dòng)擴(kuò)展的機(jī)制,更讓各種不同類(lèi)型的資源可以獨(dú)立擴(kuò)展。
寫(xiě)與生產(chǎn)者
如圖一所示,Kafka生產(chǎn)者把數(shù)據(jù)一批批地寫(xiě)到Kafka分區(qū)的主代理服務(wù)器上。而 ISR (同步復(fù)制)集合中的從代理服務(wù)器會(huì)從主代理上把記錄復(fù)制走。只有在主代理從所有的ISR集合中的副本上都收到了成功的響應(yīng)之后,一條記錄才會(huì)被認(rèn)為是成功寫(xiě)入的??梢耘渲米屔a(chǎn)者只等待主代理的響應(yīng),還是等待ISR集合中的所有代理的響應(yīng)。
DistributedLog中則有兩種方式把數(shù)據(jù)寫(xiě)入DistributedLog流,一是用一個(gè)Thrift的瘦客戶端通過(guò)寫(xiě)代理(眾所周知的多寫(xiě)入)寫(xiě)入,二是通過(guò)DistributedLog的核心庫(kù)來(lái)直接與存儲(chǔ)節(jié)點(diǎn)交互(眾所周知的單獨(dú)寫(xiě)入)。第一種方式很適合于構(gòu)建消息系統(tǒng),第二種則適用于構(gòu)建復(fù)制狀態(tài)機(jī)。你可以查閱DistributedLog文檔的相關(guān)章節(jié)來(lái)獲取更多的信息和 參考 ,以找到你需要的方式。
日志流的屬主會(huì)并發(fā)地以BookKeeper條目的形式向Bookie中寫(xiě)入一批記錄,并等待多個(gè)Bookie的Quorum結(jié)果。Quorum的大小取決于BookKeeper賬目的 ack_quorum_size 參數(shù),并且可以配置到DistributedLog流的級(jí)別。它提供了和Kafka生產(chǎn)者相似的在持久性上的靈活性。在接下來(lái)的“復(fù)制”一節(jié)我們會(huì)對(duì)比兩者在復(fù)制算法上的更多不同之處。
Kafka和DistributedLog都支持端到端的批量操作和壓縮機(jī)制。但兩者之間的一點(diǎn)微妙區(qū)別是對(duì)DistributedLog的寫(xiě)入操作都是在收到響應(yīng)之前都先通過(guò)fsync刷到硬盤(pán)上的,而我們并沒(méi)發(fā)現(xiàn)Kafka也提供了類(lèi)似的可靠性保證。
讀與消費(fèi)者
Kafka消費(fèi)者從主代理服務(wù)器上讀出數(shù)據(jù)記錄。這個(gè)設(shè)計(jì)的前提就是主代理上在大多數(shù)情況下最新的數(shù)據(jù)都還在文件系統(tǒng)頁(yè)緩存中。從充分利用文件系統(tǒng)頁(yè)緩存和獲得高性能的角度來(lái)說(shuō)這是一個(gè)好辦法。
DistributedLog則采用了完全不同的方法。因?yàn)楦鱾€(gè)存儲(chǔ)節(jié)點(diǎn)之間沒(méi)有明確的主從關(guān)系,DistributedLog可以從任意存儲(chǔ)著相關(guān)數(shù)據(jù)的存儲(chǔ)節(jié)點(diǎn)上讀出數(shù)據(jù)。為了獲得可預(yù)期的低延遲,DistributedLog引入了一個(gè)推理式讀機(jī)制,即在超出了配置的讀操作時(shí)限之后,它會(huì)在不同的副本上再次嘗試獲取數(shù)據(jù)。這就可能會(huì)對(duì)存儲(chǔ)節(jié)點(diǎn)導(dǎo)致比Kafka更高的讀壓力。不過(guò),如果將讀超時(shí)時(shí)間配成可以讓99%的存儲(chǔ)節(jié)點(diǎn)的讀操作都不會(huì)超時(shí),那就可以極大程度地解決延遲問(wèn)題,只帶來(lái)1%的額外讀壓力。
對(duì)于讀的考慮和機(jī)制上的不同主要源于復(fù)制機(jī)制和存儲(chǔ)節(jié)點(diǎn)的I/O系統(tǒng)的不同,在下文會(huì)繼續(xù)討論。
復(fù)制
Kafka用的是ISR復(fù)制算法:將一個(gè)代理服務(wù)器選為主。所有寫(xiě)操作都被發(fā)送到主代理上,所有處于ISR集合中的從代理都從主代理上讀取和復(fù)制數(shù)據(jù)。主代理會(huì)維護(hù)一個(gè)高水位線(HW,High Watermark),即每個(gè)分區(qū)最新提交的數(shù)據(jù)記錄的偏移量。高水位線會(huì)不斷同步到從代理上,并周期性地在所有代理上記錄檢查點(diǎn),以備恢復(fù)之用。在所有ISR集合中的副本都把數(shù)據(jù)寫(xiě)入了文件系統(tǒng)(并不必須是磁盤(pán))并向主代理發(fā)回了響應(yīng)之后,主代理才會(huì)更新高水位線。