Zookeeper源碼分析(二) ----- zookeeper日誌

zookeeper源碼分析系列文章:html

原創博客,純手敲,轉載請註明出處,謝謝!java

既然咱們是要學習源碼,那麼如何高效地學習源代碼呢?答案就是跟蹤日誌。說到zookeeper日誌,小編以爲很是的重要,若是沒有日誌文件,後期的zookeeper事務處理基本上不能玩。所以,我將zookeeper日誌放在源碼分析系列的第二部分,也是在可以運行zookeeper的基礎上,進行日誌分析。apache

1、zookeeper日誌類型

zookeeper中有兩類日誌,分別是:api

  • 一、事務日誌log
  • 二、快照日誌snapshot

事務日誌 : 顧名思義,就是用於存放事務執行的相關信息,如zxidcxid等。至於什麼是zookeeper事務,放到後面的文章中講。bash

快照日誌 : ``zookeeper數據節點數據是運行在內存中的,固然內存保存這些結點的數據不可能無限大,並且數據節點的內容是動態變化的,所以zookeeper提供一中獎數據節點持久化的機制,每隔一段時間,zookeeper會將內存中的數據節點DataTree序列到磁盤中,所以就造成了咱們的快照日誌。session

zookeeper源碼調試的過程當中,諸如服務端的啓動日誌、客戶端的啓動日誌、請求的相關日誌,因爲zookeeper採用log4j進行日誌打印,所以log4j必須在類路徑下查找log4j.properties文件,若是啓動的過程當中找不到該文件,則報錯以下:app

log4j:WARN No appenders could be found for logger (org.apache.log4j.jmx.HierarchyDynamicMBean).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
複製代碼

所以,必須將log4j.properties放到類路徑下,那麼對於可惡的ant工程,類路徑在哪裏呢?我也不清楚,因而我直接在eclipse右鍵工程Build Path->Configure build path找到類路徑爲%baseDir%\src\java\main,具體以下圖:eclipse

因此將conf目錄下的log4j.properties拷貝一份至上圖的目錄中便可!工具

2、zookeeper日誌可視化

上面說到zookeeper中有兩種日誌類型,但很遺憾,上面的日誌你都沒法直接看到,由於都是採用二進制進行保存的。查看zookeeper源代碼也能夠知道,zookeeper日誌輸出體如今FileTxnLog.java文件的append()方法中,具體以下:源碼分析

// 寫日誌文件,採用log. + 哈希值的命名形式保存文件
logFileWrite = new File(logDir, ("log." + Long.toHexString(hdr.getZxid())));
// 文件輸出流
fos = new FileOutputStream(logFileWrite);
// 採用BufferedOutputStream包裹fos成緩衝輸出流
logStream=new BufferedOutputStream(fos);
// 這是重點,這裏採用org.apache.jute.BinaryOutputArchive二進制構件對logStream進行包裹,輸出二進制數據
oa = BinaryOutputArchive.getArchive(logStream);
複製代碼

對應的文件系統的文件以下圖:

既然是二進制日誌文件,那麼咱們直接打開該文件確定是亂碼嘛!怎麼辦呢?下面提供兩種方法,這兩種方法都是基於zookeeper提供的LogFormatter.java工具類來實現的。

  • 一、在eclipse中開該類,而後運行該類的main方法的同時傳入你想查看的日誌文件路徑便可
  • 二、採用命令行java -classpath xxx.jar org.apache.zookeeper.server.LogFormatter logFilePath的形式進行查看

第一種方式:

這裏假設個人日誌文件存放路徑爲E:\\resources\\zookeeper-3.4.11\\conf\\log\\version-2\\log.1,當我在eclipse中運行main方法時,設置傳入的參數便可,以下圖:

而後就能夠在控制檯輸出日誌了,輸出樣例以下:

ZooKeeper Transactional Log File with dbid 0 txnlog format version 2
18-4-30 下午08時39分23秒 session 0x100022b44190000 cxid 0x0 zxid 0x1 createSession 30000

18-4-30 下午08時39分55秒 session 0x100022b44190000 cxid 0x0 zxid 0x2 closeSession null
EOF reached after 2 txns.
複製代碼

第二種方式:

  • 一、在任意目錄下新建一個臨時目錄,隨便命名爲logSee,將slf4j-api-1.6.1.jarzookeeper-3.4.11.jar兩個文件放到該目錄下,而後打開命令行,執行java -classpath .;slf4j-api-1.6.1.jar;zookeeper-3.4.11.jar org.apache.zookeeper.server.LogFormatter E:\\resources\\zookeeper-3.4.11\\conf\\log\\version-2\\log.1

其中兩個jar包之間採用分號;分隔而不是冒號,org.apache.zookeeper.server.LogFormatter表示LogFormatter.java的全路徑命名(即包全名+類名),E:\\resources\\zookeeper-3.4.11\\conf\\log\\version-2\\log.1表示你想查看的日誌文件。

輸出以下:

3、zookeeper日誌清理機制

zookeeper是將日誌以文件的形式存放在磁盤中,長此以往,磁盤的文件就愈來愈多,zookeeper提供了一種按期清理日誌和快照文件的機制。

QuorumPeerMain.javazookeeper的啓動類,在zookeeper啓動以前會建立一個DatadirCleanupManager對象進行數據清理任務,DatadirCleanupManager會根據你配置文件的配置決定是否清理以及每隔多久進行清理,其底層原理採用JDKTimer定時器實現,下面將對其實現機制從源碼的角度進行分析:

先看QuorumPeerMain類,在其initializeAndRun()方法執行時會建立一個DatadirCleanupManager對象,並將zoo.cfg配置文件的相關配置傳遞給該對象,源碼以下:

protected void initializeAndRun(String[] args)
        throws ConfigException, IOException
    {
        QuorumPeerConfig config = new QuorumPeerConfig();
        if (args.length == 1) {
            config.parse(args[0]);
        }

        // 啓動數據目錄清理定時任務,傳遞的參數有數據目錄DataDir,日誌目錄LogDir,以及快照保留數量count,默認>3,最後一個是每一個多長時間進行清理
        DatadirCleanupManager purgeMgr = new DatadirCleanupManager(config
                .getDataDir(), config.getDataLogDir(), config
                .getSnapRetainCount(), config.getPurgeInterval());
        purgeMgr.start();

        if (args.length == 1 && config.servers.size() > 0) {
            runFromConfig(config);
        } else {
            LOG.warn("Either no config or no quorum defined in config, running "
                    + " in standalone mode");
            // there is only server in the quorum -- run as standalone
            ZooKeeperServerMain.main(args);
        }
    }
複製代碼

你能夠在配置文件zoo.cfg文件中增長兩個配置,分別是autopurge.snapRetainCountautopurge.snapRetainCountautopurge.purgeInterval就是設置多少小時清理一次。而autopurge.snapRetainCount是設置保留多少個快照文件snapshot,以前的多有都刪除。

有一點性能問題就是,通常zookeeper是運行在集羣中,業務會比較繁忙,若是每隔多久去清理勢必會影響性能,咱們會想可否有一種在集羣不繁忙的時候去執行清理操做,好比在每晚的12點。可是很遺憾,zookeeper並無提供相應的實現,zookeeper採用Timer的方式去實現,而不是像quartz,Spring同樣提供cron表達式配置。但注意,並非說Timer不能實現指定時間執行,而是說zookeeper沒有實現而已。由於quartz,Spring底層仍是使用TimerExecutors去實現的嘛!

再來看看DatadirCleanupManagerstart()方法:

public void start() {
        if (PurgeTaskStatus.STARTED == purgeTaskStatus) {
            LOG.warn("Purge task is already running.");
            return;
        }
        // Don't schedule the purge task with zero or negative purge interval. if (purgeInterval <= 0) { LOG.info("Purge task is not scheduled."); return; } // 建立TIMER timer = new Timer("PurgeTask", true); // 建立定時任務 TimerTask task = new PurgeTask(dataLogDir, snapDir, snapRetainCount); // 每隔多少小時執行 timer.scheduleAtFixedRate(task, 0, TimeUnit.HOURS.toMillis(purgeInterval)); purgeTaskStatus = PurgeTaskStatus.STARTED; } 複製代碼

如今原理一目瞭然了,日誌源碼分析就先到這了,同時你也閱讀完了,很是棒!歡迎評論區留言,多多交流!

相關文章
相關標籤/搜索