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; } 复制代码

如今原理一目了然了,日志源码分析就先到这了,同时你也阅读完了,很是棒!欢迎评论区留言,多多交流!

相关文章
相关标签/搜索