本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源、有趣、入门级的 ZooKeeper 教程,面向有编程基础的新手。 项目地址:https://github.com/HelloG…

本文作者:HelloGitHub-老荀
Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源、有趣、入门级的 ZooKeeper 教程,面向有编程基础的新手。
项目地址:https://github.com/HelloGitHub-Team/HelloZooKeeper
今儿就带大家打入 ZooKeeper 的源码内部!
一、源码调试
授人以鱼不如授人以渔
我始终相信 “纸上得来终觉浅”,最终读者想要自己真正了解到 ZK 内部原理,阅读源码还是必不可少的,如果你们和我一样也拥有肉眼 Debug 的能力,那其实可以不用大费周章搭建源码调试环境,直接正面硬刚。
但是如果没有的话,把 ZK 源码下载下来,使用称手的 IDE 直接跑起来,然后在需要学习的地方直接打断点,岂不是美滋滋
1.1 下载源码
ZooKeeper 3.6.2 源码下载页面
上面的链接中随便选一个下载速度快的,点击下载压缩包即可,下载完成后解压缩就会得到如下的目录结构
1 | . |
目录中是有
1 | pom.xml |
所以 ZK 需要通过 maven 编译整个项目,先确保自己的 maven 是安装好的
1 | $ mvn --version |
如果有这样的输出说明 maven 是安装成功的,具体安装过程我这里就略过了,如果你有困难的话,可以留言给我们
1.2 编译项目
进入和
1 | pom.xml |
同级目录中并输入
1 | $ mvn install -DskipTests=true |
就会看到项目在进行编译了,等到最后的输出
1 | BUILD SUCCESS |
,就说明项目编译完成了
1 | [INFO] Reactor Summary: |
1.3 打开并配置项目
之后就可以通过你的 IDE 打开这个目录了,我这里使用的是 idea
然后开始配置
1 | Run/Debug Configurations |
点击
1 | + |
添加新的配置

选择
1 | Application |
1.3.1 单机版启动配置
然后配置按照下图去填写或选择
先给这个配置起一个牛逼的名字
选择
1 | Modify options |
打开子菜单
确保图中菜单中的三个子选项都被选中(前面有 √)
然后我们看具体的配置
在我电脑上解压缩后的项目路径为
1 | /Users/junjiexun/Desktop/apache-zookeeper-3.6.2 |
读者请根据自己情况修改
选择你本地 jdk (我本地是 1.8 其他版本的不知道行不行,低版本肯定是不行,因为源码中用到了 1.8 的一些写法)
选择
1 | zookeeper |
配置
1 | VM options |
,内容为
1 | -Dlog4j.configuration=file:/Users/junjiexun/Desktop/apache-zookeeper-3.6.2/conf/log4j.properties |
,如果不配置的话,无法输出日志
指定启动类
1 | org.apache.zookeeper.server.ZooKeeperServerMain |
单机版启动需要命令行参数,内容为
1 | 2181 /Users/junjiexun/Desktop/apache-zookeeper-3.6.2/data |
这个应该是不用修改,自动就会填上的,反正内容就是
1 | /Users/junjiexun/Desktop/apache-zookeeper-3.6.2 |
点击中间的
1 | + |
添加包路径,内容为
1 | org.apache.zookeeper.server.* |
然后点击
1 | Apply |
以及
1 | OK |
完成保存。
然后点击这个小虫子就可以启动了

1 | 2021-01-22 15:12:16,319 [myid:] - INFO [main:NIOServerCnxnFactory@674] - binding to port 0.0.0.0/0.0.0.0:2181 |
看到日志输出,如果没有报错的话就是成功了!
然后我们可以用客户端测试下
1 | ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); |
输出为
1 | [zookeeper] |
单机版的搞定了!我们下面试试集群版
1.3.2 集群版启动配置
我们有时候需要调试集群版 ZK 才有的逻辑,那之前的单机版就不够用了,并且我这里推荐将之前的源码压缩包,解压到两个不同的目录下,然后通过 IDE 分别打开这两个目录,去完全模拟两个不同的节点。集群版的和单机版配置是差不多的,我们来看看有哪些不一样的吧?我这里演示就启动两个节点 myid 分别是 1 和 2。
首先将默认的
1 | zoo_sample.cfg |
复制���重���名成
1 | zoo.cfg |
,也可以直接重命名
新建
1 | data |
目录(如果没有的话),并在其下新建一个文本文件 myid 文本内容是 1
然后编辑下
1 | zoo.cfg |
:
1 | # 修改 |
具体的配置如下:
启动类不同,集群的为
1 | org.apache.zookeeper.server.quorum.QuorumPeerMain |
命令行参数不同,传入的是
1 | zoo.cfg |
路径,我的路径是
1 | /Users/junjiexun/Desktop/apache-zookeeper-3.6.2/conf/zoo.cfg |
然后是配置第二个节点,我这里假设第二个节点的项目目录是
1 | /Users/junjiexun/Desktop/apache-zookeeper-3.6.2-bak |
第二个节点把 myid 文件中的内容修改为 2
1 | zoo.cfg |
中内容是
1 | # 修改 |
命令行的参数是
1 | /Users/junjiexun/Desktop/apache-zookeeper-3.6.2-bak/conf/zoo.cfg |
其他我没提到的和节点 1 是一样的。
我们启动两个节点试试
1 | 2021-01-22 15:44:08,461 [myid:1] - INFO [QuorumPeer[myid=1](plain=[0:0:0:0:0:0:0:0]:2181)(secure=disabled):WatchManagerFactory@42] - Using org.apache.zookeeper.server.watch.WatchManager as watch manager |
最后的
1 | Peer state changed |
代表选举完成了,贴出来的这个节点 1 是 Follower,大功告成!
之后当你想要学习源码的流程的时候��直��本地启动服务端即可,是不是美滋滋呢~
1.4 源码阅读指北
服务端启动,集群
1 | QuorumPeerMain#main |
,单机
1 | ZooKeeperServerMain#main |
客户端
1 | ZooKeeper |
解析配置相关,
1 | QuorumPeerConfig#parse |
内存模型(小红本)
1 | DataTree |
回调通知(小黄本)
1 | IWatchManager |
查看该接口实现
默认实现
1 | WatchManager |
优化方案
1 | WatchManagerOptimized |
选举
1 | FastLeaderElection#lookForLeader |
服务端实例,设置流水线
1 | setupRequestProcessors |
方法
Leader 节点
1 | LeaderZooKeeperServer |
Follower 节点
1 | FollowerZooKeeperServer |
Observer 节点
1 | ObserverZooKeeperServer |
各个���水线员工
1 | RequestProcessor |
查看该接口的实现
持久化 log
1 | FileTxnLog |
,snapshot
1 | FileSnap |
会话管理
1 | SessionTrackerImpl#run |
协议
1 | Record |
查看该接口的实现
1.5 源码阅读心得
阅读大型项目的源码一定是一个费时费心费力的工作,我这里也讲一下我阅读 ZK 源码的心得:
不要死抠细节!大型项目的源码数量通常比较多,如果盯着逻辑中的每一个细节,就会迷失在源码的汪洋大海中。
通常阅读源码都要带着一个目的。例如:ZK 是怎么进行协议转换的,ZK 是怎么选举的等等。有了目的以后,看相关源码是要选择性的忽略一些其他不相关的细节,可以通过方法名或者注释,来对具体的代码块先有一个感性的认识。
碰到读不懂的地方,可以先去网上看看有没有人写过类似的博客,站在巨人的肩膀上,很可能别人一点你就通了。
在 ZK 中一般间接或者直接继承
1 | ZooKeeperThread |
都是线程对象,主要逻辑可以查看
1 | run |
方法。
任何一个类重要的属性肯定是在成员字段中,通过查看成员字段是可以大致推测出该类背后的数据结构。
成员属性中如果有阻塞队列的字段,大概率会是生产者-消费者模式的体现,可以重点关注该阻塞队列的使用,何时放入以及取出元素。
1.6 小结
我用一些图文的篇幅介绍了如何在本地调试 ZK 源码,以及如何科学的阅读源码。我本地的环境是 Mac,用的 IDE 是 idea,如果你的环境或者工具和我不一样,碰到了困难的话,也可以给我们留言哦~
二、ZK 中应用到的设计模式
ZK 本身就是分布式的应用,也是优秀的开源项目,我这里就简单聊聊我在阅读源码中看到的应用在 ZK 里的设计模式吧
2.1 生产者消费者
这个是 ZK 中非常有代表性的设计模式应用了,ZK 本身是 C/S 架构的设计,请求就是客户端发送给服务端数据,响应则是服务端发送给客户端数据,而 ZK 实现一些功能并不是通过线性顺序的去调用不同的方法去完成的,通常会由生产者线程,阻塞队列和消费者线程组成,生产者线程将上游收到的一些请求对象放入阻塞队列,当前的方法就返回了,之后由消费者线程通过循环不停的从阻塞队列中获取,再完成之后的业务逻辑。举例:
1 | PrepRequestProcessor |
,阻塞队列是
1 | submittedRequests |
1 | SyncRequestProcessor |
,阻塞队列是
1 | queuedRequests |
2.2 工厂模式
有一些接口的实现,ZK 本身提供了默认的选择,但是如果使用者在配置中配置了其他的实现的话,ZK 的工厂就会自动去创建那些其他的实现。举例:
在创建
1 | ClientCnxnSocket |
时,会根据
1 | zookeeper.clientCnxnSocket |
的配置去选择客户端的 IO 实现
在创建
1 | IWatchManager |
时,会根据
1 | zookeeper.watchManagerName |
的配置去选择服务端的 watch 管理实现
在创建
1 | ServerCnxnFactory |
时,会根据
1 | zookeeper.serverCnxnFactory |
的配置去选择服务端的 IO 工厂实现
2.3 责任链模式
之前有学习过,ZK 服务端业务逻辑处理是通过将一个个
1 | XxxProcessor |
串起来实现的,Processor 彼此不关心调用顺序,仅仅通过
1 | nextProcessor |
关联,不同的服务端角色也可以通过这种方式极大的复用代码
单机模式下:
1 | PrepRequestProcessor -> SyncRequestProcessor -> FinalRequestProcessor |
集群模式下 Leader :
1 | LeaderRequestProcessor -> PrepRequestProcessor -> ProposalRequestProcessor -> CommitProcessor -> Leader.ToBeAppliedRequestProcessor -> FinalRequestProcessor |
集群模式下 Follower :
1 | FollowerRequestProcessor -> CommitProcessor -> FinalRequestProcessor |
集群模式下 Observer :
1 | ObserverRequestProcessor -> CommitProcessor -> FinalRequestProcessor |
2.4 策略模式
1 | zookeeper.snapshot.compression.method |
可以配置成不同的 snapshot 压缩算法,当需要生成 snapshot 文件的时候,会根据不同的压缩算法去执行:
1 | gz |
:
1 | GZIPInputStream |
1 | snappy |
:
1 | SnappyInputStream |
默认:
1 | BufferedInputStream |
2.5 装饰器模式
还是刚刚的压缩算法,对外提供的是
1 | CheckedInputStream |
的统一处理对象,使用
1 | CheckedInputStream |
将上面三种压缩实现包装起来,这些对象全部都是
1 | InputStream |
的子类
1 | switch (根据不同的配置) { |
三、总结
今天我讲了如何直接从 ZK 源码 DEBUG,介绍了一些 ZK 中用到的设计模式,大家有阅读源码问题的话,欢迎给我留言哦。本文首发于 「HelloGitHub」公众号
下一期介绍 ZK 的高级用法纯实战,期待一下吧~
老规矩,如果你有任何对文章中的疑问也可以是建议或者是对 ZK 原理部分的疑问,欢迎来仓库中提 issue 给我们,或者来语雀话题讨论。
本文标题: 手摸手教你阅读和调试大型开源项目 ZooKeeper
本文作者: OSChina
发布时间: 2021年04月15日 10:16
最后更新: 2025年04月03日 11:07
原始链接: https://haoxiang.eu.org/656a0ca5/
版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0许可协议,转载请注明出处!