您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么實現(xiàn)Apache Bookkeeper中的Journal”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么實現(xiàn)Apache Bookkeeper中的Journal”吧!
充當(dāng)WAL
寫請求處理:
首先在Bookkeeper服務(wù)端收到了寫Entry的請求之后會交給Bookie
來處理
Bookie.addEntryInternal
這個方法會將請求攜帶的Entry信息寫入LedgerStorage
(實際數(shù)據(jù)存儲的位置 + 索引)
寫入成功之后還會同時將這個請求寫入Journal
。
其他重要信息:比如說Ledger
被fence的信息、LAC等。
啟動的時候?qū)AL進行replay,將記錄在journal里面的內(nèi)容重新apply到LedgerStorage
里面,
避免之前寫入LedgerStorage
的內(nèi)容因為沒有刷盤導(dǎo)致丟失。
checkpoint 邏輯
和其他WAL一樣,需要記錄一個位置,這個位置標(biāo)識著LedgerStorage
里面的數(shù)據(jù)已經(jīng)全都落盤了
這一個位置之前的WAL日志都可以被刪除。
維護JournalChannel
邏輯,寫入WAL日志,日志輪轉(zhuǎn)等。
整個寫入是異步的,寫入結(jié)果通過callback進行后續(xù)處理。
寫入的參數(shù)會封裝成為一個QueueEntry
放到寫請求隊列
class QueueEntry { // 內(nèi)容 ByteBuf entry; long ledgerId; long entryId; // 寫結(jié)果的callback WriteCallback cb; // 進入隊列的時間,用來確定是否等待時間過長 long enqueueTime; // 是否需要等內(nèi)容落盤 boolean ackBeforeSync; }
這個隊列會被一個線程定期處理,這里先叫做BookieJournalWriteThread
好了(實際沒有這個類)
取出之后會將攜帶的ByteBuffer
寫入到JournalChannel
里面。這個線程專門處理這個邏輯,
其他的活不干。
這里先說下JournalChannel
這個類,這個類可以認為是底層journal磁盤文件的映射,
內(nèi)部實現(xiàn)是一個帶讀寫緩存的FileChannel
, 寫入的時候先到寫緩存,
有相應(yīng)的邏輯主動觸發(fā)寫緩存寫到包裝的FileChannel
里面。
QueueEntry
的字節(jié)寫入之后,可能內(nèi)容在寫緩存里面。
我們需要觸發(fā)flush邏輯,將寫緩存的內(nèi)容寫到FileChannel
里面。
這里flush和 sync 到磁盤不是一個說法。
flush 是調(diào)用FileChannel.write
為了減少調(diào)用次數(shù)
sync 是調(diào)用FileChannel.force
為了fsync 到磁盤
這里觸發(fā)flush的條件有3種:
時間bound:這個請求入隊之后,一段時間之后必須被處理(寫入到channel或者落盤)
寫請求的個數(shù) || 累積的寫請求的字節(jié)數(shù)
寫請求隊列為空(一般測試的時候出現(xiàn)這個,寫請求很少的情況下大部分都會被1這個條件兜底)
滿足flush 條件則會主動將寫緩存的內(nèi)容刷到FileChannel
里面。
如果不需要等待內(nèi)容落盤(ackBeforeSync=false),則直接將callback提交到線程池執(zhí)行回調(diào)。
之后寫請求被放到一個等待flush的batch里面。
flush邏輯做完之后,會去判斷是否需要落盤。
按照配置有下面幾種條件需要落盤。
每次flush都需要落盤。
journal 文件輪轉(zhuǎn),需要把之前的文件落盤。
按照配置的interval 落盤。
如果需要落盤則這個時候會將之前的batch 封裝成為一個ForceWriteRequest
放到落盤隊列里面。
這個隊列會被ForceWriteThread
清空。
這里可以配置一個groupCommit
的邏輯。避免多次fsync
。
如果配置了這個則會將隊列里面的請求合并到一起,觸發(fā)單次的FileChannel.force
同樣,落盤之后會將之前的callback 放到線程池去處理回調(diào)。
這個邏輯比較簡單,就是啟動的時候把這個文件的內(nèi)容從上次成功checkpoint的位置開始讀取。
把讀到的內(nèi)容再次寫入到LedgerStorage
里面就ok。
這個實際上和LedgerStorage
這個是聯(lián)動的,如果這一段WAL上面的內(nèi)容,已經(jīng)被LedgerStorage
成功寫到磁盤上了,那么這段WAL就可以被刪除了。
這里會有一個LastLogMark
文件,標(biāo)記了(journal文件,offset)表示這個文件在這個offset之前的內(nèi)容可以被干掉了。
Journal
這個類實現(xiàn)了CheckpointSource
這個接口。
實際動作由SyncThread
(實現(xiàn)了Checkpointer
接口)執(zhí)行。
每種LedgerStorage
的checkpoint觸發(fā)條件不同。
entryLogPerLedgerEnabled || isDbLedgerStorage 會按照時間interval 定期觸發(fā)checkpoint
InterleavedLedgerStorage 會在日志輪轉(zhuǎn)的時候觸發(fā)
SortedLedgerStorage 會在memtable 需要flush的時候觸發(fā)
實際邏輯比較簡單
public void checkpoint(Checkpoint checkpoint) { // ... ledgerStorage.checkpoint(checkpoint); checkpointSource.checkpointComplete(checkpoint, true); // ... }
checkpointComplete 這個方法會刷新磁盤上的LastLogMarker
這個文件,同時落盤。
(主要邏輯在LedgerStorage.checkpoint
這里)
這里的磁盤是LedgerStorage的磁盤
寫入請求處理是異步的,提交之后就會被Journal線程處理。
Journal線程負責(zé)將內(nèi)容寫入Journal channel,同時按照一定條件執(zhí)行flush邏輯。
如果判斷需要進行刷盤則將刷盤batch包裝成ForceWriteRequest
ForceWriteThread
清理隊列進行g(shù)roup commit 處理。負責(zé)journal落盤。
對于寫請求的callback不會在這兩個執(zhí)行,會被額外提交到callback線程池處理。
感謝各位的閱讀,以上就是“怎么實現(xiàn)Apache Bookkeeper中的Journal”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么實現(xiàn)Apache Bookkeeper中的Journal這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。