idea zookeeper的使用_学习ZooKeeper源码,就从这篇开始吧
【ZooKeeper系列】1.ZooKeeper單機(jī)版、偽集群和集群環(huán)境搭建
【ZooKeeper系列】2.用Java實(shí)現(xiàn)ZooKeeper API的調(diào)用
在系列的前兩篇文章中,介紹了ZooKeeper環(huán)境的搭建(包括單機(jī)版、偽集群和集群),對創(chuàng)建、刪除、修改節(jié)點(diǎn)等場景用命令行的方式進(jìn)行了測試,讓大家對ZooKeeper環(huán)境搭建及常用命令行有初步的認(rèn)識,也為搭建ZooKeeper的開發(fā)環(huán)境、生產(chǎn)環(huán)境起到了拋磚引玉的作用。也介紹了用Java來實(shí)現(xiàn)API的調(diào)用,包括節(jié)點(diǎn)的增、刪、改、查。通過對這兩篇的學(xué)習(xí),讓大家對ZooKeeper的使用有了初步認(rèn)識,也可用于實(shí)現(xiàn)系列后面篇章要介紹的命名服務(wù)、集群管理、分布式鎖、負(fù)載均衡、分布式隊列等。
在前兩篇中,強(qiáng)調(diào)了閱讀英文文檔的重要性,也帶領(lǐng)大家解讀了部分官方文檔,想傳達(dá)出的理念是ZooKeeper沒有想象中的那么難,閱讀官方文檔也沒那么難。后面的篇章中,結(jié)合官方文檔,在實(shí)戰(zhàn)演練和解讀源碼的基礎(chǔ)上加深理解。
上聯(lián):說你行你就行不行也行
下聯(lián):說不行就不行行也不行
橫批:不服不行閱讀源碼就跟這個對聯(lián)一模一樣,就看你選上聯(lián),還是下聯(lián)了!
這一篇開始源碼環(huán)境的搭建。
here we go
很多老鐵留言說很想研讀些github上的開源項目,但代碼clone下來后總出現(xiàn)這樣或那樣奇奇怪怪的問題,很影響學(xué)習(xí)的積極性。學(xué)習(xí)ZooKeeper的源碼尤其如此,很多人clone代碼后,報各種錯,提示少各種包。問了下度娘ZooKeeper源碼環(huán)境,搜出來的文章真的差強(qiáng)人意,有些文章錯的竟然非常離譜。這里我重新搭建了一遍,也會介紹遇到的一些坑。
很多老鐵上來一堆猛操作,從github上下載了ZooKeeper源碼后,按常規(guī)方式導(dǎo)入IDEA,最后發(fā)現(xiàn)少各種包。起初我也是這樣弄的,以為ZooKeeper是用Maven來構(gòu)建的,仔細(xì)去了解了下ZooKeeper的版本歷史,其實(shí)是用的Ant。如今一般用的Maven或Gradle,很少見到Ant的項目了,這里不對Ant多做介紹。
1 Ant環(huán)境搭建
Ant官網(wǎng)地址:https://ant.apache.org/bindownload.cgi
下載解壓后,跟配置jdk一樣配置幾個環(huán)境變量:
//修改為自己本地安裝的目錄ANT_HOMT=D:\apache-ant-1.10.7?
PATH=%ANT_HOME%/bin
CLASSPATH=%ANT_HOME%/lib
配置好后,測試下Ant是否安裝成功。ant -version,得到如下信息則代表安裝成功:
Apache?Ant(TM)?version?1.10.7?compiled?on?September?1?2019Ant的安裝跟JDK的安裝和配置非常相似,這里不做過多介紹。
2 下載ZooKeeper源碼
源碼地址:https://github.com/apache/zookeeper
猿人谷在寫本篇文章時,releases列表里的最新版本為release-3.5.6,我們以此版本來進(jìn)行源碼環(huán)境的搭建。
3 編譯ZooKeeper源碼
切換到源碼所在目錄,運(yùn)行ant eclipse將項目編譯并轉(zhuǎn)成eclipse的項目結(jié)構(gòu)。
在這里插入圖片描述這個編譯過程會比較長,差不多等了7分鐘。如果編譯成功,會出現(xiàn)如下結(jié)果:在這里插入圖片描述
4 導(dǎo)入IDEA
上面已經(jīng)將項目編譯并轉(zhuǎn)成eclipse的項目結(jié)構(gòu),按eclipse的形式導(dǎo)入項目。
在這里插入圖片描述在這里插入圖片描述5 特別說明
將源碼導(dǎo)入IDEA后在org.apache.zookeeper.Version中發(fā)現(xiàn)很多紅色警告,很明顯少了org.apache.zookeeper.version.Info類。
在這里插入圖片描述查詢源碼得知是用來發(fā)布的時候生成版本用的,我們只是研讀源碼,又不發(fā)布版本所以直接寫死就ok了。在這里插入圖片描述
即新增Info類:package?org.apache.zookeeper.version;
public?interface?Info?{
????int?MAJOR?=?3;
????int?MINOR?=?5;
????int?MICRO?=?6;
????String?QUALIFIER?=?null;
????String?REVISION_HASH?=?"c11b7e26bc554b8523dc929761dd28808913f091";
????String?BUILD_DATE?=?"10/08/2019?20:18?GMT";
}
6 啟動zookeeper
針對單機(jī)版本和集群版本,分別對應(yīng)兩個啟動類:
單機(jī):ZooKeeperServerMain
集群:QuorumPeerMain
這里我們只做單機(jī)版的測試。
在conf目錄里有個zoo_sample.cfg,復(fù)制一份重命名為zoo.cfg。
zoo.cfg里的內(nèi)容做點(diǎn)修改(也可以不做修改),方便日志查詢。dataDir和dataLogDir根據(jù)自己的情況設(shè)定。
dataDir=E:\\02private\\1opensource\\zk\\zookeeper\\dataDirdataLogDir=E:\\02private\\1opensource\\zk\\zookeeper\\dataLogDir
運(yùn)行主類 ?org.apache.zookeeper.server.ZooKeeperServerMain,將zoo.cfg的完整路徑配置在Program arguments。
在這里插入圖片描述運(yùn)行ZooKeeperServerMain,得到的結(jié)果如下:Connected?to?the?target?VM,?address:?'127.0.0.1:0',?transport:?'socket'
log4j:WARN?No?appenders?could?be?found?for?logger?(org.apache.zookeeper.jmx.ManagedUtil).
log4j:WARN?Please?initialize?the?log4j?system?properly.
log4j:WARN?See?http://logging.apache.org/log4j/1.2/faq.html#noconfig?for?more?info.
告知日志無法輸出,日志文件配置有誤。這里需要指定日志文件log4j.properties。
在這里插入圖片描述在VM options配置,即指定到conf目錄下的log4j.properties:-Dlog4j.configuration=file:E:/02private/1opensource/zk/zookeeper/conf/log4j.properties
配置后重新運(yùn)行ZooKeeperServerMain,輸出日志如下,
在這里插入圖片描述可以得知單機(jī)版啟動成功,單機(jī)版服務(wù)端地址為127.0.0.1:2181。
7 啟動客戶端
通過運(yùn)行ZooKeeperServerMain得到的日志,可以得知ZooKeeper服務(wù)端已經(jīng)啟動,服務(wù)的地址為127.0.0.1:2181。啟動客戶端來進(jìn)行連接測試。
客戶端的啟動類為org.apache.zookeeper.ZooKeeperMain,進(jìn)行如下配置:
在這里插入圖片描述即客戶端連接127.0.0.1:2181,獲取節(jié)點(diǎn)/yuanrengu的信息。
下面帶領(lǐng)大家一起看看客戶端啟動的源碼(org.apache.zookeeper.ZooKeeperMain)。這里要給大家說下我閱讀源碼的習(xí)慣,很多老鐵以為閱讀源碼就是順著代碼看,這樣也沒啥不對,只是很多開源項目代碼量驚人,這么個干看法,容易注意力分散也容易看花眼。我一般是基于某個功能點(diǎn),從入口開始debug跑一遍,弄清這個功能的“代碼線”,就像跑馬圈塊地兒一樣,弄清楚功能有關(guān)的代碼,了解參數(shù)傳遞的過程,這樣看代碼時就更有針對性,也能排除很多干擾代碼。
7.1 main
main里就兩行代碼,通過debug得知args里包含的信息就是上面我們配置在Program arguments里的信息:
在這里插入圖片描述7.1.1 ZooKeeperMain
????public?ZooKeeperMain(String?args[])?throws?IOException,?InterruptedException?{????????//?用于解析參數(shù)里的命令行的
????????cl.parseOptions(args);
????????System.out.println("Connecting?to?"?+?cl.getOption("server"));
????????//?用于連接ZooKeeper服務(wù)端
????????connectToZK(cl.getOption("server"));
????}
通過下圖可以看出,解析參數(shù)后,就嘗試連接127.0.0.1:2181,即ZooKeeper服務(wù)端。cl.getOption("server")得到的就是127.0.0.1:2181。
在這里插入圖片描述7.1.2 parseOptions
在這里插入圖片描述可以很清楚的得知解析args的過程,主要從"-server","-timeout","-r","-"這幾個維度來進(jìn)行解析。7.1.3 connectToZK
????protected?void?connectToZK(String?newHost)?throws?InterruptedException,?IOException?{????????//?用于判斷現(xiàn)在ZooKeeper連接是否還有效
????????//?zk.getState().isAlive()?注意這個會話是否有效的判斷,客戶端與?Zookeeper連接斷開不一定會話失效
????????if?(zk?!=?null?&&?zk.getState().isAlive())?{
????????????zk.close();
????????}
????????//?此時newHost為127.0.0.1:2181
????????host?=?newHost;
????????//?判斷是否為只讀模式,關(guān)于只讀模式的概念在前一篇文章中有介紹
????????boolean?readOnly?=?cl.getOption("readonly")?!=?null;
????????//?用于判斷是否建立安全連接
????????if?(cl.getOption("secure")?!=?null)?{
????????????System.setProperty(ZKClientConfig.SECURE_CLIENT,?"true");
????????????System.out.println("Secure?connection?is?enabled");
????????}
????????zk?=?new?ZooKeeperAdmin(host,?Integer.parseInt(cl.getOption("timeout")),?new?MyWatcher(),?readOnly);
????}
ZKClientConfig.SECURE_CLIENT已經(jīng)被標(biāo)注為deprecation了:
????/**?????*?Setting?this?to?"true"?will?enable?encrypted?client-server?communication.
?????*/
????@SuppressWarnings("deprecation")
????public?static?final?String?SECURE_CLIENT?=?ZooKeeper.SECURE_CLIENT;
debug查看關(guān)鍵點(diǎn)處的信息,可以得知這是建立一個ZooKeeper連接的過程(【ZooKeeper系列】2.用Java實(shí)現(xiàn)ZooKeeper API的調(diào)用)
下圖看看幾處關(guān)鍵信息:
在這里插入圖片描述Integer.parseInt(cl.getOption("timeout"))為30000。
至此完成了ZooKeeperMain main = new ZooKeeperMain(args);的整個過程。簡短點(diǎn)說就是:
解析Program arguments里的參數(shù)
連接ZooKeeper服務(wù)端
7.2 main.run()
敲黑板,重頭戲來了哦!
一起來看下run()的代碼:
????void?run()?throws?CliException,?IOException,?InterruptedException?{????????//?cl.getCommand()得到的是?“get”,就是上文傳進(jìn)來的
????????if?(cl.getCommand()?==?null)?{
????????????System.out.println("Welcome?to?ZooKeeper!");
????????????boolean?jlinemissing?=?false;
????????????//?only?use?jline?if?it's?in?the?classpath
????????????try?{
????????????????Class>?consoleC?=?Class.forName("jline.console.ConsoleReader");
????????????????Class>?completorC?=
????????????????????Class.forName("org.apache.zookeeper.JLineZNodeCompleter");
????????????????System.out.println("JLine?support?is?enabled");
????????????????Object?console?=
????????????????????consoleC.getConstructor().newInstance();
????????????????Object?completor?=
????????????????????completorC.getConstructor(ZooKeeper.class).newInstance(zk);
????????????????Method?addCompletor?=?consoleC.getMethod("addCompleter",
????????????????????????Class.forName("jline.console.completer.Completer"));
????????????????addCompletor.invoke(console,?completor);
????????????????String?line;
????????????????Method?readLine?=?consoleC.getMethod("readLine",?String.class);
????????????????while?((line?=?(String)readLine.invoke(console,?getPrompt()))?!=?null)?{
????????????????????executeLine(line);
????????????????}
????????????}?catch?(ClassNotFoundException?e)?{
????????????????LOG.debug("Unable?to?start?jline",?e);
????????????????jlinemissing?=?true;
????????????}?catch?(NoSuchMethodException?e)?{
????????????????LOG.debug("Unable?to?start?jline",?e);
????????????????jlinemissing?=?true;
????????????}?catch?(InvocationTargetException?e)?{
????????????????LOG.debug("Unable?to?start?jline",?e);
????????????????jlinemissing?=?true;
????????????}?catch?(IllegalAccessException?e)?{
????????????????LOG.debug("Unable?to?start?jline",?e);
????????????????jlinemissing?=?true;
????????????}?catch?(InstantiationException?e)?{
????????????????LOG.debug("Unable?to?start?jline",?e);
????????????????jlinemissing?=?true;
????????????}
????????????if?(jlinemissing)?{
????????????????System.out.println("JLine?support?is?disabled");
????????????????BufferedReader?br?=
????????????????????new?BufferedReader(new?InputStreamReader(System.in));
????????????????String?line;
????????????????while?((line?=?br.readLine())?!=?null)?{
????????????????????executeLine(line);
????????????????}
????????????}
????????}?else?{
????????????//?處理傳進(jìn)來的參數(shù)
????????????processCmd(cl);
????????}
????????System.exit(exitCode);
????}
通過下圖可以看出processCmd(cl);里cl包含的信息:
在這里插入圖片描述debug到processCmd(MyCommandOptions co) 就到了決戰(zhàn)時刻。里面的processZKCmd(MyCommandOptions co)就是核心了,代碼太長,只說下processZKCmd里的重點(diǎn)代碼,獲取節(jié)點(diǎn)/yuanrengu的信息:在這里插入圖片描述
因為我之前沒有創(chuàng)建過/yuanrengu節(jié)點(diǎn),會拋異常org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /yuanrengu , 如下圖所示:在這里插入圖片描述
經(jīng)過上面的步驟后exitCode為1,執(zhí)行System.exit(exitCode);退出。
至此帶領(lǐng)大家dubug了一遍org.apache.zookeeper.ZooKeeperMain,上面我說過,閱讀源碼干看效果很小,只有debug才能有助于梳理流程和思路,也能清楚參數(shù)傳遞的過程發(fā)生了什么變化。
溫馨提示
上面我們介紹了源碼環(huán)境的搭建過程,運(yùn)行運(yùn)行主類 ?org.apache.zookeeper.server.ZooKeeperServerMain 啟動ZooKeeper服務(wù)端,運(yùn)行org.apache.zookeeper.ZooKeeperMain連接服務(wù)端。
閱讀源碼最好能動起來(debug)讀,這樣代碼才是活的,干看的話代碼如死水一樣,容易讓人索然無味!
每個人操作的方式不一樣,有可能遇到的問題也不一樣,搭建過程中遇到什么問題,大家可以在評論區(qū)留言。
如果希望更多ZooKeeper的文章,務(wù)必留言告知
總結(jié)
以上是生活随笔為你收集整理的idea zookeeper的使用_学习ZooKeeper源码,就从这篇开始吧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 导入文件夹_MySQL-导入
- 下一篇: java合并list_怎么把两个list