提要
Apache Bookkeeper github地址:https://github.com/apache/bookkeeper
本源碼分析基于bookkeeper 4.14版本
術(shù)語說明
0. bk: Bookkeeper中bookie節(jié)點的簡稱
1. ledger: Bookkeeper中分布式日志分段,由多個fragment組成
2. e : 為了簡述,文中e(E)表示fragment的ensemble size,即fragment的全部可寫bookie集合大小。文中部分語境e指fragment的全部可寫bookie集合。
3. qw: 為了簡述,文中qw(Qw)表示entry的write quorum size,即entry的寫集合大小。文中部分語境qw指entry的寫集合。文中entry讀取時的讀集合等同于寫集合。
4. qa: 為了簡述,文中qa(Qa)表示entry的ack?quorum size,即entry寫入時至少qa數(shù)量的bk返回寫入成功時bk client即可認為entry寫入成功。
背景
Apache Bookkeeper通過entry的多副本存儲策略保證數(shù)據(jù)安全。當e=3,qw=3,qa=2時,Bookkeeper承諾entry存儲的副本數(shù)為2或3。當bk宕機或下線時,必然導致該bk上的ledger中entry的副本數(shù)減1。當qa數(shù)量的bk宕機或下線時,理論上會導致數(shù)據(jù)丟失。因此Bookkeeper社區(qū)提供AutoRecovery模塊保證數(shù)據(jù)存儲副本數(shù)始終>=qa。
此外,在Apache Pulsar中,broker作為bk client將消息持續(xù)寫入bk中。對于OPEN狀態(tài)的ledger,當>=qa數(shù)量的bookie下線,如果無消息寫入或者寫入頻次很低,會等到寫入請求到來時才會觸發(fā)報錯創(chuàng)建新的ledger。如果在此之前該ledger從broker A分配到Broker B會觸發(fā)recover,但ledger的多個bookie節(jié)點(>=2)已下線因此recover必定失敗,出現(xiàn)異常ledger(即異常topic)。社區(qū)autoRecovery模塊也可解決該問題。
原理
autoRecovery模塊有兩個主要組件:
注:下文按e=3,qw=3,qa=2策略展開
Auditor:
1. autoRecovery會選主啟動Auditor,通過ledgers/underreplication/auditorelection目錄下的臨時節(jié)點選主,序列號最小的啟動Auditor
2. 監(jiān)聽ledgers/available目錄和ledgers/available/readonly目錄,若bookie節(jié)點下線則觸發(fā)審計
3. 監(jiān)聽ledgers/underreplication/lostBookieRecoveryDelay目錄,若目錄節(jié)點數(shù)據(jù)改變則觸發(fā)審計
4. 審計:獲取全部的ledgermetadata并按bookie進行分類(Map<BookieId, Set<ledgerId>>),將下線bookie對應(yīng)的ledgers寫入目錄ledgers/underreplication/ledgers/,如下:

ReplicationWorker:
1. 各autoRecovery節(jié)點對等,每個節(jié)點單線程處理ledger
2. 從ledgers/underreplication/ledgers/(可看作等待處理的ledger隊列)目錄下獲取一個ledger,加鎖,加鎖成功表示該autoRecovery可處理,加鎖失敗則嘗試下一個ledger。

3. 處理ledger,可分為如下兩種情況:
當ledger狀態(tài)為CLOSED時,如下圖,

a: 篩選出ledger A中entry不可讀的fragment:每個fragment的entry數(shù)量可以通過后一個fragment或LastEntryId-startEntryId獲得,通過percentageOfLedgerFragmentToBeVerified將這些entry分成若干等份,每份隨機取一個entry作為集合B(若percentageOfLedgerFragmentToBeVerified=0則只取首尾兩個entry),讀取集合B的entry若讀取失敗則認為對應(yīng)fragment需要復制,讀取失敗節(jié)點bk-H即是檢測到的下線節(jié)點。
注:
b: 針對需要復制的fragment:從頭到尾分批次讀取entry,讀取成功后將entry寫到新的節(jié)點Bk-F
c: 修改原數(shù)據(jù)將fragment的ensemble里的Bk-H替換為Bk-F
d: 可解決影響數(shù)據(jù)丟失的風險
當ledger狀態(tài)為OPEN/IN_RECOVERY時,如下圖:

a: fragment1-3的修復同CLOSED的ledger。
b: fragment4為正在寫的,若對應(yīng)bookie節(jié)點都在線則無需處理。若存在bookie節(jié)點下線則需要處理:recover該open狀態(tài)的ledger,目的是通過fence操作奪取broker的寫權(quán)限。最后再close該ledger。
c: 可解決因bk下線或宕機導致的recover失敗風險。
bk下線
如上為社區(qū)autoRecovery原理,對應(yīng)社區(qū)bookie下線步驟如下:
$ bin/bookkeeper shell decommissionbookie -bookieid <lost bookieid>
該命令僅僅觸發(fā)審計,并等待下線bookieid對應(yīng)的ledger復制完畢。而下線bk的ledger處理則由autoRecovery完成。
如果不執(zhí)行上述命令,會等待ledgers/underreplication/lostBookieRecoveryDelay配置時間觸發(fā)審計。
參考文檔:decomission