zookeeper源码分析系列文章:

  • Zookeeper源码分析(一) ----- 源码运行环境搭建

原创博客,纯手敲,转载请注明出处,谢谢!

既然我们是要学习源码,那么如何高效地学习源代码呢?答案就是跟踪日志。说到zookeeper日志,小编觉得非常的重要,如果没有日志文件,后期的zookeeper事务处理基本上不能玩。因此,我将zookeeper日志放在源码分析系列的第二部分,也是在能够运行zookeeper的基础上,进行日志分析。

一、zookeeper日志类型

zookeeper中有两类日志,分别是:

  • 1、事务日志log
  • 2、快照日志snapshot

事务日志 : 顾名思义,就是用于存放事务执行的相关信息,如zxidcxid等。至于什么是zookeeper事务,放到后面的文章中讲。

快照日志 : ``zookeeper数据节点数据是运行在内存中的,当然内存保存这些结点的数据不可能无限大,而且数据节点的内容是动态变化的,因此zookeeper提供一中奖数据节点持久化的机制,每隔一段时间,zookeeper会将内存中的数据节点DataTree序列到磁盘中,因此就形成了我们的快照日志。

zookeeper源码调试的过程中,诸如服务端的启动日志、客户端的启动日志、请求的相关日志,由于zookeeper采用log4j进行日志打印,因此log4j必须在类路径下查找log4j.properties文件,如果启动的过程中找不到该文件,则报错如下:

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,具体如下图:

所以将conf目录下的log4j.properties拷贝一份至上图的目录中即可!

二、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工具类来实现的。

  • 1、在eclipse中开该类,然后运行该类的main方法的同时传入你想查看的日志文件路径即可
  • 2、采用命令行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 3000018-4-30 下午08时39分55秒 session 0x100022b44190000 cxid 0x0 zxid 0x2 closeSession null
EOF reached after 2 txns.
复制代码

第二种方式:

  • 1、在任意目录下新建一个临时目录,随便命名为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表示你想查看的日志文件。

输出如下:

三、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 standaloneZooKeeperServerMain.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;}// 创建TIMERtimer = new Timer("PurgeTask", true);// 创建定时任务TimerTask task = new PurgeTask(dataLogDir, snapDir, snapRetainCount);// 每隔多少小时执行timer.scheduleAtFixedRate(task, 0, TimeUnit.HOURS.toMillis(purgeInterval));purgeTaskStatus = PurgeTaskStatus.STARTED;}
复制代码

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

Zookeeper源码分析(二) ----- zookeeper日志相关推荐

  1. zookeeper源码分析之恢复事务日志

    zookeeper源码分析之恢复事务日志 前言 源码分析 查看事务日志命令 总结 前言 本文是基于zookeeper集群启动过程分析(https://blog.csdn.net/weixin_4244 ...

  2. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  3. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  4. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  5. SpringBoot源码分析(二)之自动装配demo

    SpringBoot源码分析(二)之自动装配demo 文章目录 SpringBoot源码分析(二)之自动装配demo 前言 一.创建RedissonTemplate的Maven服务 二.创建测试服务 ...

  6. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  7. gSOAP 源码分析(二)

    gSOAP 源码分析(二) 2012-5-24 flyfish 一 gSOAP XML介绍 Xml的全称是EXtensible Markup Language.可扩展标记语言.仅仅是一个纯文本.适合用 ...

  8. Android Q 10.1 KeyMaster源码分析(二) - 各家方案的实现

    写在之前 这两篇文章是我2021年3月初看KeyMaster的笔记,本来打算等分析完KeyMaster和KeyStore以后再一起做成一系列贴出来,后来KeyStore的分析中断了,这一系列的文章就变 ...

  9. 【投屏】Scrcpy源码分析二(Client篇-连接阶段)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

最新文章

  1. UI:UITableView表视图
  2. java子网划分_子网划分讲解及练习(二)
  3. Python Django列表渲染for的使用
  4. C# asp:Repeater DataSource ListT
  5. 如何容器化你的 ASP.Net Core
  6. mysql数据库安全机制研究意义_MySQL数据库的安全机制
  7. c 语言 realloc 源码,C语言,realloc
  8. oracle4030,oracle ora-4030错误求解
  9. mysql vs连不上_vs2015下配置MySQL,使之能使用c++连接完美运行
  10. Python入门--第三方模块的安装与使用,pip,import
  11. 获取公网ip,获取用户城市地址
  12. 26.leetcode160_intersection_of_two_linked_lists
  13. 电大计算机应用基础实训任务1-4,计算机应用基础本形考任务1
  14. scrapy运行报错: Overridden settings XXX
  15. Magento(CE1.X)自带模块解析七
  16. java开发人员的小习惯
  17. Spring boot 中使用 Thymeleaf
  18. vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5
  19. 学习 Python Django
  20. c语言 字符金字塔问题

热门文章

  1. 分布式开放消息系统(RocketMQ)的原理与实践
  2. git rebase -i 汇合提交
  3. C++ MFC常用函数(转)
  4. OpenCV之gpu 模块. 使用GPU加速的计算机视觉:GPU上的相似度检测(PNSR 和 SSIM)
  5. Coursera课程Python for everyone:Quiz: Single-Table SQL
  6. Coursera课程Python for everyone:chapter3
  7. 图像代数运算:平均值去噪,减去背景
  8. C语言中 if 和 else if 的区别
  9. leetcode 697 Degree of an Array
  10. 【RegExp】JavaScript中正则表达式判断匹配规则以及常用方法