Flume-ng FileChannel原理解析

boylook發表於2013-09-15



對於Flume來說主要有兩個ChannelMemoryFile;對於線上環境主要以FileChannel為主,因此這裡主要討論它的實現:

FileChannel裡主要由一個WALlog和一個記憶體佇列組成:

FileChannelQueue主要又以下幾個部分組成:

private final EventQueueBackingStore backingStore;

private final InflightEventWrapper inflightTakes;

private final InflightEventWrapper inflightPuts;

其中backingStore代表了queue在持久化存在,使用了記憶體對映檔案的方式;每次對queue的讀寫操作都記錄在backingStoreoverwritemapupdate in place)中,當進行checkpoint的時候合併到elementsBuffer並持久化到磁碟;所有未提交的正在讀寫資料都分別儲存在inflight結構中,當checkpoint時一併進行持久化,為回滾時使用;

inflight中儲存了transactionid->fileid以及transactionid->eventptr的對映,具體儲存在backingStore裡的則是eventptrfileid,offset;

Checkpoint file的檔案結構如下:

File Header1029 bytes

Eventptr;

File header裡前8個位元組儲存了版本號,接下來24個位元組是sequeuece no.(類似rdbmsscn),接下來4個位元組儲存了checkpoint的狀態;

作為WALLog主要儲存了(transactionid,sequenceNo,Event,每次讀寫都先在log裡寫入event,對於寫操作會拿到eventptr放入queue中;而commitrollback操作在log中的記錄形式是(transactionid,sequenceNoOP={commit,rollback})

這兩個結構主要是體現在FileBackedTransaction中如下

FileBackedTransaction extends BasicTransactionSemantics

......

LinkedBlockingDeque takeList;

LinkedBlockingDeque putList;

long transactionID;

Log log;

FlumeEventQueue queue: EventQueueBackingStoreFile

其中queue = log.getFlumeEventQueue();

首先看put/take path以及commit

1. doPut(Event event)->

queue.addWithoutCommit(ptr, transactionID)

log.put(transactionID, event)->

synchronized LogFile.Writer.put(ByteBuffer buffer)

putList.offer(ptr)

2. doTake() ->

FlumeEventPointer ptr = queue.removeHead(transactionID);

takeList.offer(ptr),

log.take(transactionID, ptr); ->

synchronized LogFile.Writer.take(ByteBuffer buffer)

Event event = log.get(ptr);

3. doCommit()->

if(puts > 0) {

log.commitPut(transactionID);

synchronized (queue) {

while(!putList.isEmpty()) {

queue.addTail(putList.removeFirst())

queue.completeTransaction(transactionID);

}

}

else if (takes > 0) {

log.commitTake(transactionID);->

logFileWriter.commit(buffer);

logFileWriter.sync();

queue.completeTransaction(transactionID);

queueRemaining.release(takes);

}

}

從上面的程式碼可以看出,對於每一個put/take都會記錄一條oploglog,commit的時候會對log進行sync到磁碟持久化,同時會把event指標存放到queue上;這裡的log就類似於mysql裡的binlog(binlog_format=statement),而這裡的queue存放的是指向event的指標;

簡例:FileChannel如下,對FileChannel put2個訊息,a,b;則在log,queue裡的儲存狀態如下,Log裡儲存了(transactionid,sequenceNoEvent),queue則儲存了eventptr

Queue:ptr->a,ptr->b

WAL log:(1,1,put a),(1,2,put b),(1,3,commit)

當例項crash時,透過log來恢復queue的狀態,類似rdbms一樣,replay是很耗時的操作,因此會定期對queue進行checkpoint

Log在初始化的時候會啟動一個排程執行緒workerExecutor,由排程執行緒定期(checkpoint interval)排程一個backgroupWorkder來進行非強制性checkpoint

Log.writeCheckpoint(Boolean force): tryLockExclusive->

synchronized queue.checkpoint->

backingStore.beginCheckpoint();//檢查是否checkpoint正在進行;同時進行標記checkpoint開始,並同步MMAP file;

inflightPuts.serializeAndWrite();//

inflightTakes.serializeAndWrite();//inflightputs/takes序列化並寫到相應檔案

backingStore.checkpoint();->

setLogWriteOrderID(WriteOrderOracle.next());

writeCheckpointMetaData();

//copy from overwriteMap to elementsBuffer(MMAP)

//標記checkpoint結束,並同步檔案

簡例:接上例,在a,b提交後,這時進行了一次checkpoint(儲存在磁碟上的checkpoint則是2個指標ptr->a,ptr->b),此時scn=4;之後,又完成了一個take transaction ,ptr to a 也同時被刪除;如果這時Flume crashqueuecheckpoint中重建,並且取得checkpoint scn=4,replay這之後的log進行crash recovery;在恢復後,立刻執行一次checkpoint.

queue:ptr->b

WAL log:(1,1,put a),(1,2,put b),(1,3,commit),(2,5,take a),(2,6,commit)

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/11676357/viewspace-1060911/,如需轉載,請註明出處,否則將追究法律責任。

相關文章