溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么實現(xiàn)Apache Bookkeeper中的Journal

發(fā)布時間:2021-12-24 10:31:52 來源:億速云 閱讀:169 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“怎么實現(xiàn)Apache Bookkeeper中的Journal”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么實現(xiàn)Apache Bookkeeper中的Journal”吧!

主要功能

  1. 充當(dāng)WAL

  • 寫請求處理:
    首先在Bookkeeper服務(wù)端收到了寫Entry的請求之后會交給Bookie來處理
    Bookie.addEntryInternal 這個方法會將請求攜帶的Entry信息寫入LedgerStorage(實際數(shù)據(jù)存儲的位置 + 索引)
    寫入成功之后還會同時將這個請求寫入Journal。

  • 其他重要信息:比如說Ledger被fence的信息、LAC等。

  1. 啟動的時候?qū)AL進行replay,將記錄在journal里面的內(nèi)容重新apply到LedgerStorage里面,
    避免之前寫入LedgerStorage的內(nèi)容因為沒有刷盤導(dǎo)致丟失。

  2. checkpoint 邏輯
    和其他WAL一樣,需要記錄一個位置,這個位置標(biāo)識著LedgerStorage里面的數(shù)據(jù)已經(jīng)全都落盤了
    這一個位置之前的WAL日志都可以被刪除。

  3. 維護JournalChannel邏輯,寫入WAL日志,日志輪轉(zhuǎn)等。

大致邏輯

1. 寫入:

整個寫入是異步的,寫入結(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)容在寫緩存里面。

flush 邏輯

我們需要觸發(fā)flush邏輯,將寫緩存的內(nèi)容寫到FileChannel里面。

這里flush和 sync 到磁盤不是一個說法。
flush 是調(diào)用FileChannel.write 為了減少調(diào)用次數(shù)
sync 是調(diào)用FileChannel.force 為了fsync 到磁盤

這里觸發(fā)flush的條件有3種:

  1. 時間bound:這個請求入隊之后,一段時間之后必須被處理(寫入到channel或者落盤)

  2. 寫請求的個數(shù) || 累積的寫請求的字節(jié)數(shù)

  3. 寫請求隊列為空(一般測試的時候出現(xiàn)這個,寫請求很少的情況下大部分都會被1這個條件兜底)

滿足flush 條件則會主動將寫緩存的內(nèi)容刷到FileChannel里面。
如果不需要等待內(nèi)容落盤(ackBeforeSync=false),則直接將callback提交到線程池執(zhí)行回調(diào)。
之后寫請求被放到一個等待flush的batch里面。

flush邏輯做完之后,會去判斷是否需要落盤。

落盤(ForceWrite)邏輯

按照配置有下面幾種條件需要落盤。

  1. 每次flush都需要落盤。

  2. journal 文件輪轉(zhuǎn),需要把之前的文件落盤。

  3. 按照配置的interval 落盤。

如果需要落盤則這個時候會將之前的batch 封裝成為一個ForceWriteRequest 放到落盤隊列里面。

這個隊列會被ForceWriteThread 清空。

這里可以配置一個groupCommit的邏輯。避免多次fsync
如果配置了這個則會將隊列里面的請求合并到一起,觸發(fā)單次的FileChannel.force
同樣,落盤之后會將之前的callback 放到線程池去處理回調(diào)。

2. replay 邏輯

這個邏輯比較簡單,就是啟動的時候把這個文件的內(nèi)容從上次成功checkpoint的位置開始讀取。
把讀到的內(nèi)容再次寫入到LedgerStorage 里面就ok。

3. checkpoint 邏輯

這個實際上和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)注!

向AI問一下細節(jié)

免責(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)容。

AI