总体而言,Zookeeper服务端的日志分为三种:事务日志、快照日志、log4j日志。
log4j日志无需多言,我们在%ZOOKEEPER_DIR%/conf/log4j.properties中配置了日志的详细信息。
本文主要介绍下事务日志的内容和Zookeeper如何生成事务日志以及其作用。快照日志的话,下一篇会着重介绍。
我们在%ZOOKEEPER_DIR%/conf/zoo.cfg中配置的dataDir参数,是专门用于存储事务日志和快照日志的文件夹路径。当然,我们也可以将两个日志分开(事务读写比较频繁时事务日志会比较大,将两者分开可以提高系统性能),这时可以在zoo.cfg中配置dataLogDir路径。
那么什么是事务日志呢?
就是Zookeeper服务端针对客户端的所有事务请求(create、update、delete)等操作,在返回成功之前,都会将本次操作的内容持久化到磁盘上,完成之后,才返回客户端成功标志。
在笔者的机器上,我们在%ZOOKEEPER_DIR%/data/version-2目录下,看到以下几个文件
这个就是事务日志,直接打开的话是二进制内容,不利于查看,那么我们可以通过Zookeeper源码中提供的org.apache.zookeeper.server.LogFormatter来查看,
通过在main()方法中指定需要查看的事务日志文件路径即可以查看,笔者在查看log.1文件时,生成以下输出:
通过以上日志可以很清楚的看到每一次事务操作时的具体信息,这样方便我们进行问题排查。
当然,不仅可以直接通过debug代码的方式来查看,我们同样可以通过Zookeeper.jar的方式来查看。大家可以参考这篇博文: https://blog.csdn.net/qq_34291777/article/details/86644347
有了前面对Zookeeper server端处理请求的分析,我们知道事务日志的添加调用入口是通过SyncRequestProcessor来完成的。下来就一起来分析下其是如何将事务日志落入磁盘的。
我们就以create()方法为示例,来看下整个过程。前面server处理会话创建请求的文章中,我们知道,最终交由三个requestProcessor来处理,处理顺序为 PrepRequestProcessor --> SyncRequestProcessor --> FinalRequestProcessor
3.1 PrepRequestProcessor.pRequest() 创建事务请求对象
总结:事务请求对象Request,包含请求头TxnHeader hdr和请求体Record txn,所以PrepRequestProcessor的主要工作就是堆hdr和txn的封装
3.2 SyncRequestProcessor 事务日志添加
事务日志的磁盘写入,默认分为两步:写入(append)、刷新(rollLog/commit)
写入动作并不是真正的写入磁盘(而是暂时缓存下来),刷新操作才是真正将缓存的内容写入到磁盘中。
有了以上的分析,我们后面直接去分析append方法和flush方法的执行过程
主要就是对ZKDatabase.append()方法和ZKDatabase.rollLog()方法的调用
4.1 ZKDatabase相关方法
本质上都交由snapLog来操作
4.2 FileTxnSnapLog相关方法
FileTxnSnapLog本质上只是一个包装类,统一提供对事务日志和快照日志的操作API。
4.3 FileTxnLog 事务日志操作
在分析代码之前,我们先看下FileTxnLog类的注释,可以帮助我们很好的理解事务日志文件的组成,如下图所示:
事务日志文件主要由三部分组成:文件头(FileHead)、事务内容(Txn组成的list,每一个Txn包含了checksum Txnlen TxnHeader Record 0x42等属性)、填充数字
事务内容的组成,如下图所示:
总结:append()的过程本质上就是将事务请求体不断写入的过程,按照标准的流操作执行即可。
而关于commit()等方法,就更简单了,就是执行流的flush操作。笔者不再赘述。
本文分析了Zookeeper事务日志的相关知识点,从如何查看到源码分析其写入过程。代码并不算复杂,了解了该日志的基本信息后,我们在日常的问题排查中就可以考虑查看事务日志来还原客户端操作过程。
后续会继续对快照日志进行分析。