学习源码里的优秀设计思想,比如一些疑难问题的解决思路,还有一些优秀的设计模式,整体提升自己的技术功底。
源码看多了,对于一个新技术或框架的掌握速度会有大幅提升,看下框架demo大致就能知道底层的实现,技术框架更新再快也不怕。
遇到线上问题,特别是框架源码里的问题(比如bug),能够快速定位,这就是相比其他没看过源码的人的优势。
面试一线互联网公司对于框架技术一般都会问到源码级别的实现。
对技术有追求的人必做之事,使用了一个好的框架,很想知道底层是如何实现的。
参与到开源项目的研发,结识更多大牛,积累更多优质人脉。
先看官方文档快速掌握框架的基本使用。
找一个demo入手,顺藤摸瓜快速静态看一遍框架的主线源码,画出源码主流程图,切勿一开始就陷入源码的细枝末节,否则会把自己绕晕,凭经验猜。
总结框架的一些核心功能点,从这些功能点入手深入到源码的细节,边看源码边画源码走向图,并对关键源码的理解做笔记,把源码里的闪光点都记录下来,后续借鉴到工作项目中,理解能力强的可以直接看静态源码,也可以边看源码边debug源码执行过程,观察一些关键变量的值。
所有功能点的源码都分析完后,回到主流程图再梳理一遍,争取把自己画的所有图都在脑袋里做一个整合。
ZK源码下载地址: https://github.com/apache/zookeeper.git
选择分支3.5.8
。
bin:启动脚本。比如常用的命令:zkServer.sh、zkCli.sh等。
conf:配置文件。比如zoo.cfg。
zookeeper-cient:C语言的客户端源码。
zookeeper-jute:序列化代码。
zookeeper-recipes:示例代码。
zookeeper-server:服务端源码。
源码导入idea后,org.apache.zookeeper.Version
类会报错,需要建一个辅助类:
package org.apache.zookeeper.version;
public interface Info {
int MAJOR = 1;
int MINOR = 0;
int MICRO = 0;
String QUALIFIER = null;
int REVISION = -1;
String REVISION_HASH = "1";
String BUILD_DATE = "2020-10-15";
}
然后在根目录编译执行:
mvn clean install -DskipTests
开源项目找入口类一般都是从启动脚本去找,可以从bin目录下的zkServer.sh
或zkServer.cmd
里找到启动主类运行即可:
org.apache.zookeeper.server.quorum.QuorumPeerMain
1、将conf文件夹里的zoo_sample.cfg
文件复制一份改名为zoo.cfg
,将zoo.cfg
文件位置配置到启动参数里,类似于linux安装并启动zk的步骤:
2、启动之前需要先将zookeeper-server项目里pom.xml文件里依赖的包(除了jline)的scope为provided这一行全部注释掉
3、将conf文件夹里的log4j.properties文件复制一份到zookeeper-server项目的\target\classes
目录下,这样项目启动时才会打印日志
用客户端命令连接源码启动的server:
bin/zkCli.sh -server 192.168.x.x:2181 # 改为自己的内网地址
从源码里运行客户端(org.apache.zookeeper.ZooKeeperMain
),注意需要加入启动参数,见下图:
从源码启动zookeeper集群:
复制3个zoo.cfg文件,修改对应集群配置,并在data目录里分别建各自的myid文件填入机器id,并创建三个不同配置的启动节点。
分别启动每个节点。类似于linux配置zk集群。
总结:
整个zookeeper选举底层可以分为选举应用层和消息传输层,应用层有自己的队列统一接收和发送选票,传输层也设计了自己的队列,但是按发送的机器分了队列,避免给每台机器发送消息时相互影响,比如某台机器如果出问题发送不成功则不会影响对正常机器的消息发送;
默认通过NIO通讯,可在启动参数中设置为Netty通讯,监听客户端的请求,官方推荐Netty,对应端口2181;
ServerSocket只能是myid大的节点连接myid小的节点,防止两个节点互相连接,因为ServerSocket只要建立了连接,就可以双向通讯(全双工);
leader会周期性给follower发送心跳ping;
投票比较逻辑:
先比较选举纪元(选大的)》纪元相等时,比较事务id(zxid越大数据越新)》事务id相等时,比较设置的节点myid(选myid大的),具体代码 如下:
protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {
LOG.debug("id: " + newId + ", proposed id: " + curId + ", zxid: 0x" +
Long.toHexString(newZxid) + ", proposed zxid: 0x" + Long.toHexString(curZxid));
if(self.getQuorumVerifier().getWeight(newId) == 0){
return false;
}
/*
* We return true if one of the following three cases hold:
* 1- New epoch is higher
* 2- New epoch is the same as current epoch, but new zxid is higher
* 3- New epoch is the same as current epoch, new zxid is the same
* as current zxid, but server id is higher.
*/
return ((newEpoch > curEpoch) ||
((newEpoch == curEpoch) &&
((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));
}