最近開始了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。