您好,登錄后才能下訂單哦!
這篇文章主要介紹“Zookeeper事務日志預分配空間源碼分析”,在日常操作中,相信很多人在Zookeeper事務日志預分配空間源碼分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Zookeeper事務日志預分配空間源碼分析”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
事務日志的添加,我們需要從FileTxnLog.append()方法看起
public class FileTxnLog implements TxnLog { volatile BufferedOutputStream logStream = null; volatile OutputArchive oa; volatile FileOutputStream fos = null; // 追加事務日志 public synchronized boolean append(TxnHeader hdr, Record txn) throws IOException { if (hdr == null) { return false; } if (hdr.getZxid() <= lastZxidSeen) { LOG.warn("Current zxid " + hdr.getZxid() + " is <= " + lastZxidSeen + " for " + hdr.getType()); } else { lastZxidSeen = hdr.getZxid(); } // 默認logStream為空 if (logStream==null) { if(LOG.isInfoEnabled()){ LOG.info("Creating new log file: " + Util.makeLogName(hdr.getZxid())); } // 以下代碼為創建事務日志文件 // 根據當前事務ID來創建具體文件名,并寫入文件頭信息 logFileWrite = new File(logDir, Util.makeLogName(hdr.getZxid())); fos = new FileOutputStream(logFileWrite); logStream=new BufferedOutputStream(fos); oa = BinaryOutputArchive.getArchive(logStream); FileHeader fhdr = new FileHeader(TXNLOG_MAGIC,VERSION, dbId); fhdr.serialize(oa, "fileheader"); // Make sure that the magic number is written before padding. logStream.flush(); filePadding.setCurrentSize(fos.getChannel().position()); streamsToFlush.add(fos); } // 預分配代碼在這里 filePadding.padFile(fos.getChannel()); byte[] buf = Util.marshallTxnEntry(hdr, txn); if (buf == null || buf.length == 0) { throw new IOException("Faulty serialization for header " + "and txn"); } Checksum crc = makeChecksumAlgorithm(); crc.update(buf, 0, buf.length); oa.writeLong(crc.getValue(), "txnEntryCRC"); Util.writeTxnBytes(oa, buf); return true; } }
創建FileTxnLog對象時,其logStream屬性為null,所以當第一次處理事務請求時,會先根據當前事務ID來創建一個文件。
public class FilePadding { long padFile(FileChannel fileChannel) throws IOException { // 針對新文件而言,newFileSize=64M long newFileSize = calculateFileSizeWithPadding(fileChannel.position(), currentSize, preAllocSize); if (currentSize != newFileSize) { // 將文件擴充到64M,全部用0來填充 fileChannel.write((ByteBuffer) fill.position(0), newFileSize - fill.remaining()); currentSize = newFileSize; } return currentSize; } // size計算 public static long calculateFileSizeWithPadding(long position, long fileSize, long preAllocSize) { // If preAllocSize is positive and we are within 4KB of the known end of the file calculate a new file size // 初始時候position=0,fileSize為0,preAllocSize為系統參數執行,默認為64M if (preAllocSize > 0 && position + 4096 >= fileSize) { // If we have written more than we have previously preallocated we need to make sure the new // file size is larger than what we already have // Q:這里確實沒看懂... if (position > fileSize) { fileSize = position + preAllocSize; fileSize -= fileSize % preAllocSize; } else { fileSize += preAllocSize; } } return fileSize; } }
預分配的過程比較簡單,就是看下當前文件的剩余空間是否<4096,如果是,則擴容。
Q:
這里有一個不太明白的問題,position > fileSize的場景是怎樣的呢?
通過上述代碼分析我們知道,當logStream=null時,就會創建一個新的事務日志文件,那么logStream對象什么時候為空呢?
搜索代碼,只看到FileTxnLog.rollLog()方法會主動將logStream設置為null
public class FileTxnLog implements TxnLog { public synchronized void rollLog() throws IOException { if (logStream != null) { this.logStream.flush(); this.logStream = null; oa = null; } } }
那么根據這個線索,我們來搜索下rollLog的調用鏈
SyncRequestProcessor.run() -> ZKDatabase.rollLog() -> FileTxnSnapLog.rollLog() -> FileTxnLog.rollLog()
最終看到是在SyncRequestProcessor.run()方法中發起調用的,而且只有這一條調用鏈,我們來分析下
public class SyncRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { public void run() { try { int logCount = 0; setRandRoll(r.nextInt(snapCount/2)); while (true) { ... if (si != null) { // 追加事務日志 if (zks.getZKDatabase().append(si)) { logCount++; if (logCount > (snapCount / 2 + randRoll)) { setRandRoll(r.nextInt(snapCount/2)); // 注意:在這里發起了rollLog zks.getZKDatabase().rollLog(); ... } } else if (toFlush.isEmpty()) { ... } toFlush.add(si); if (toFlush.size() > 1000) { flush(toFlush); } } } } catch (Throwable t) { handleException(this.getName(), t); running = false; } LOG.info("SyncRequestProcessor exited!"); } }
需要注意下rollLog()方法執行的條件,就是logCount > (snapCount / 2 + randRoll)
snapCount是一個系統參數,System.getProperty("zookeeper.snapCount"),默認值為100000
randRoll是一個隨機值
那么該條件觸發的時機為:處理的事務請求數至少要大于50000。
這時就出現了一個筆者無法理解的情況:
通過對事務日志的觀察可以看到其都是64M,而至少處理50000次事務請求后,Zookeeper才會分配一個新的事務日志文件,那么這個snapCount是一個經驗值嘛?
如果事務請求的value信息都很大,那么可能到不了50000次,就會超過64M,理論上應該要創建一個新的文件了,但是貌似并沒有,這個該怎么處理呢?
如果事務請求value信息都很小,那么即使到了50000次,也不會超過64M,那么之前預分配的文件大小就浪費了一部分。
到此,關于“Zookeeper事務日志預分配空間源碼分析”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。