为什么要看源码?当然是装逼啊!哈哈
说实在博主之前看Spring源码之前没细想过这个问题,只是听大佬们说【Spring是一个非常优秀的源码】,甚至【Java程序员一定要看的系列】的地步。于是奔着这些噱头,我还是抱着积极的心态去认真学习的(3分为装逼,7分为学习)。
那ZK源码,有必要学习吗?Spring再怎么说也是基石之一,还可以说的过去,ZK呢?它在市面上又并非是无可替代的。所以一开始我是觉得没必要去学习的,而且不少人还评价ZK源码写的很抽象、不规范,可读性差。但是我的培训老师们又比较力推,所以就跟着学习了部分源码。此后,我突然间有点明悟,为什么要学习ZK源码了。原因如下:
以上是我的一些思考。同样,这个问题,我的老师也给出了他们的答案,在这里分享给大家:
Q1:为什么要学习源码?
答:
- 提升技术功底:学习源码里的优秀设计思想,比如一些疑难问题的解决思路,还有一些优秀的设计模式,整体提升自己的技术功底
- 深度掌握技术框架:源码看多了,对于一个新技术或框架的掌握速度会有大幅提升,看下框架demo大致就能知道底层的实现,技术框架更新再快也不怕
- 快速定位线上问题:遇到线上问题,特别是框架源码里的问题(比如bug),能够快速定位,这就是相比其他没看过源码的人的优势
- 对面试大有裨益:面试一线互联网公司对于框架技术一般都会问到源码级别的实现
- 知其然知其所以然:对技术有追求的人必做之事,使用了一个好的框架,很想知道底层是如何实现的
- 拥抱开源社区:参与到开源项目的研发,结识更多大牛,积累更多优质人脉
看源码方法:
我跟着老师看了一下源码,说实在,思考了很久实在不知道怎么给大家伙记下来分享给大家,有点无从下手的感觉,因为相关性太大了。所以,这里算是自结吧,主要是为了加深个人理解,以及方便后续回头看。

我在之前的笔记当中有写过选举原理,但是有点囫囵吞枣,后来才知道原来这个挺重要的,算是ZK比较重点的内容之一。现在这里重新讲解一番。
什么是Leader选举
ZooKeeper的Leader选举过程是基于投票和对比规则的,确保集群中选出一个具有最高优先级的服务器作为Leader来处理客户端请求,以及同步数据给集群中的其他节点。
选举规则
选举投票对比规则如下:
epoch:表示ZooKeeper服务器的逻辑时期(logical epoch),它是一个相对时间的概念,用于区分不同的Leader选举周期。
zxid:是一个64位的整数,由高32位的epoch和低32位的counter组成。
counter:是一个在每个时期(epoch)内递增的计数器,用于标识事务的顺序。
选举流程
首先得说明的是,ZK的Leader选举是分两步的,所以又叫:两阶段选举。为什么需要两个阶段?接下来我们看一下流程,这个流程是我们网上能搜索到的,大家都清楚的流程:(流程按照上面的模型图。即:假设集群中有3个节点,并且只启动了2个节点,第3个节点没启动)
epoch肯定是一样的,包括zxid,毕竟还没有接受过客户端的读写,所以唯一的差异就在myId上了epoch、zxid、myId,所以会优先将票投给自己,并且广播出去;当然也能接收到别的服务器【投票自己的广播】。所以,myid=1的机器按照投票规则,投票vote=(1, 0),并且收到myid=2的投票vote=(2, 0);同时myid=2的机器按照投票规则,投票vote=(2, 0),并且收到myid=1的投票vote=(1, 0)。显然,目前投票情况是服务1跟服务2各收到一张选票,所以Leader没办法选举出来myid=1的服务器经过比较之后,发现myid=2比自己更适合当Leader,于是在第二轮投票的时候投票vote=(2,0);而myid=2的服务也经过比较之后,觉得还是自己适合做Leader,于是在第二轮投票的时候投票vote=(2,0)。就这样,myid=2的服务收到了2张选票,2 > (3/2),符合【过半机制】,于是成为了Leadermyid=3的节点上线,发现已经有Leader了,那不用投票了,直接把自己置为Follower(也许有人问,为什么不重新选举?因为没必要啊,对于ZK集群来说,尽快对外服务才是重点,重新选举不是浪费时间嘛)OK,流程回顾就到这里。相信大伙通过这个业务流程去理解代码,将会事半功倍。
说明:我估计很多人源码入口都找不到,给大家一个方法,也是很多源码阅读的办法。那就是从启动脚本找,比如ZK,我们知道它的启动脚本为zkServer.sh,那就在里面找好了。
ZK源码入口类:org.apache.zookeeper.server.quorum.QuorumPeerMain。
下面是是一个自结的源码流程图,不是很好看。感兴趣的大伙,可以跟着我的流程图看一遍源码

看完选举源码之后的一些总结与思考:
run方法中实现各自领域的逻辑。每个线程通常各自维护了一条阻塞队列,线程之间交换数据是将消息发送至对应的阻塞队列中。也许,这就是JVM级别,不基于中间件的线程通信的可靠手段之一吧sockert是传输层协议,http是应用层协议,且两者安全性方面前者更高。下面是来自gpt的回答:

接下来再给大家画一下,选举的相关模型图。
整个zookeeper选举底层可以分为【选举逻辑层】和【选举消息传输层】,【逻辑层】有自己的队列统一接收和发送选票,【传输层】也设计了自己的队列,但是按发送的机器分了队列,避免给每台机器发送消息时相互影响,比如某台机器如果出问题发送不成功则不会影响对正常机器的消息发送。
