最近开始了Zookeeper的源码阅读和分析,也从如今开始把以前和如今学习到的一些Zookeeper的源码知识和个人一些理解放到博客上。不得不说这是本身第一次去完整的看一个开源项目的完整源码,从开始的第一步感受就遇到了坑= =并且多少还有些面对庞大代码的茫然。在整个过程当中(过程还没完,到如今为止)零零散散看了很多博客的分析,可是感受都是针对某个小部分的分析,但愿本身能从头至尾把本身看的过程都写下来,若是之后有别的同窗也想完整的了解了解Zookeeper的底层原理,但愿个人博客能有点抛砖引玉的做用!php
我主要参考了两篇博客:Zookeeper的源码阅读建议和Zookeeper源码阅读--环境搭建、启动服务demo。可是有个巨大巨大巨大的坑。。。不要从git上直接拉最新的代码,即便是master分支的!能够直接从Zookeeper的下载页面下载成熟的版本,想看最新的代码直接下最新发布的版本就能够了。为何要这样呢???这里有点血泪教训。。。我记得前段时间从git上拉最新的代码下来按照博客里说的方式去跑单机版的server,死活启动不起来。。。各类修改办法都试过了,都没有效果。最后我尝试着下了成熟的发布版本,而后一跑就能够了。因此建议你们千万下载成熟的发布版本。html
我这边简单说下包中类的大体做用,由于我也没有彻底看完,因此这里会持续更新:java
jute包:是Zookeeper使用的序列化工具Jute相关的。node
common包:公共工具类;git
client/server包:和client/server逻辑处理相关的类;apache
cli包:接收并执行用户输入的各类命令;json
jmx包:jmx监控;网络
还有一些zookeeper包里的类:session
Watcher/WathcedEvent:和监听事件有关的接口和类;工具
Zookeeper/ZookeeperMain:用户和Zookeeper交互;
正题开始。。。Jute对我本身也是比较陌生的,以前也没有接触过。也是在看Zookeeper代码的时候边看边查了一些,这边大体的总结下Jute的用法和在Zookeeper里的一些代码。
@InterfaceAudience.Public public interface Record { public void serialize(OutputArchive archive, String tag) throws IOException; public void deserialize(InputArchive archive, String tag) throws IOException; }
全部须要序列化的类都必须实现Record接口。在serialize和deserialize方法中,OutputArchive/InputArchive类是Jute底层真正用来作序列化和反序列化的类,而且它们能够为多个对象进行序列化/反序列化操做,这也是方法中tag存在的做用,用来标识对象。
下面是org.apache.zookeeper.data.ID的serialize和deserialize方法的实现。
public void serialize(OutputArchive a_, String tag) throws java.io.IOException { a_.startRecord(this,tag); a_.writeString(scheme,"scheme"); a_.writeString(id,"id"); a_.endRecord(this,tag); } public void deserialize(InputArchive a_, String tag) throws java.io.IOException { a_.startRecord(tag); scheme=a_.readString("scheme"); id=a_.readString("id"); a_.endRecord(tag); }
能够看到其实和其余的序列化工具同样,也都是一个写一个读。
特别的是这里OutputArchive和InputArchive也是接口。其中他们的实现类以下:
Binary的实现通常是为了网络传输和本地磁盘存储的,也是最底层的序列化方式;
CSV的实现更多的是为了方便数据对象的可视化展示;
XML的是为了把数据保存为XML格式的文件。
特别的是:
在Zookeeper中有一个zookeeper.jute文件,里面定义了全部实体类的包,类名以及该类全部的成员变量及其类型。以下:
module org.apache.zookeeper.data { class Id { ustring scheme; ustring id; } class ACL { int perms; Id id; } ...
Jute会根据这个配置文件生成一些类,这些都实现了Record接口且都在generated包下。可是对于生成类的详细步骤我也没有深刻研究,也只知道个大概,由于Jute确实没有在别的地方见到用过,可是整体的生成步骤大概就是根据配置文件的内容去生成的,负责生成类的那些类在src/java/generated/org/apache/zookeeper下,有兴趣能够看下。
zookeeper的通讯协议是基于TCP/IP的,和http的报文的基本格式仍是挺像的。都主要由请求头和请求主体组成。
len | header | body |
---|---|---|
0-3 | xid(4-7) + type(8-11) | len(12-15) + path(16-totalLen-1) + watch(totalLen) |
请求头类是RequestHeader,也是Jute生成的类。
@InterfaceAudience.Public public class RequestHeader implements Record { private int xid; private int type; ...
请求头中xid是记录客户端请求发起的前后序号,用来标识单个客户端请求的前后顺序;
type表明的是请求的操做类型,对应的数字存储在OpCode接口中,种类太多了就不粘贴上来了。
public class ConnectRequest implements Record { private int protocolVersion; private long lastZxidSeen; private int timeOut; private long sessionId; private byte[] passwd; ...
public class GetDataRequest implements Record { private String path; private boolean watch;
public class SetDataRequest implements Record { private String path; private byte[] data; private int version;
请求体里主要也就是这三种类型,很简单,并且都是Jute生成的。具体数据是什么看一下域的命名就知道了。
public class ReplyHeader implements Record { private int xid; private long zxid; private int err;
zxid表明服务端最新的事务id,err是错误码。
public class ConnectResponse implements Record { private int protocolVersion; private int timeOut; private long sessionId; private byte[] passwd;
public class GetDataResponse implements Record { private byte[] data; private org.apache.zookeeper.data.Stat stat;
Stat类存的是znode的相关信息。
public class SetDataResponse implements Record { private org.apache.zookeeper.data.Stat stat;
这些和请求的都是一一对应的,很简单。
去查Jute资料的时候了解到了挺多先进的序列化工具相似fastjson,protobuf,之后能够深刻看看。
我把我当时看的一些地方都简单说了说,由于这些地方都是些实体类,没有太多逻辑,因此没有详细介绍逻辑的部分,也算是抛砖引玉,若是想深刻看看这个部分,能够参考下面的link。