隨著軟件開發的規模逐漸增大,傳統的單體服務已經解決不了逐漸增大的併發流量問題,項目改造為分佈式已經成了一種趨勢,伴隨而來的是分佈式協調相關的一系列問題,而apache的開源組件--zookeeper則是一款成熟’、熱門的分佈式協調組件,從本篇開始,zk的學習之旅正式開始
zk是什麼
在學習zk之前,我們先了解zk到底是什麼?按照官方的說法,ZooKeeper是一個分佈式的,開放源碼的分佈式應用程序協調服務,是Google的Chubby一個開源的實現,是Hadoop和Hbase的重要組件。它是一個為分佈式應用提供一致性服務的軟件,提供了包括:配置維護、域名服務、分佈式同步、組服務等相關功能。
zk的下載與安裝
接下來我們開始下載zookeeper,這裡有兩個下載方式,第一個方式則是在zookeeper官網下載,最新版的下載地址如下:
https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.5.6/
而第二種方式則是在gitHub中搜索Zookeeper,如下:
點擊進入後,選擇releases進入正式發佈的版本中,選擇你需要下載的版本,即可
下載完畢後,我們將下載好的zookeeper-xxx.tar.gz解壓到常用安裝目錄下面即可,然後將conf目錄下的zoo_sample.cfg文件,複製一份,重命名為zoo.cfg,如下:
接著,我們打開zoo.cfg文件,將dataDir=/tmp/zookeeper修改成zookeeper安裝目錄所在的data文件夾(安裝完以後需要手動創建zk的data文件夾和log存放的目錄),並且添加dataLogDir(日誌存放目錄),如下:
配置完畢後,我們來啟動zk服務,雙擊運行bin目錄下的zkServer.cmd文件即可。
編譯zk源碼
除了前面的直接下載zk的正式版以外,我們也可以選擇下載zk源碼,進行編譯,然後配置啟動zk服務端,此種方式的好處是zk的所有代碼和啟動類代碼等都可以進行自定義編寫和修改,但是較為繁瑣,我們先來了解一下:
1.zk源碼需要使用Ant進行編譯,所以我們需要進入Ant官網下載對應的版本,進行配置
下載地址如下:http://ant.apache.org/bindownload.cgi,這裡需要注意的是我們下載的ant版本分為jdk1.5兼容版本和jdk1.8版本,我們需要按照當前開發環境的jdk版本以及需要編譯的zk源碼的版本與之對應,點擊下載:
=
下載完成後,我們將其解壓,開始配置環境變量,例如:
<code>1. `ANT_HOME=E:\\apache-ant-1.10.5`
/<code>
並且我們需要在全局的path路徑下添加一個%ANT_HOME%\\bin ,配置完畢後,我們進入cmd窗口,執行如下命令:
<code>
1. `C:\\Users\\Administrator>ant -version`
2. `ApacheAnt(TM) version 1.10.7 compiled on September12019`
/<code>
可以看到成功返回了ant的版本信息,代表此時配置已經成功。
2.進入github進行下載,我們找到當前所有的 ****branchs:
找到我們需要版本的分支,進行git下載:
<code>
1. `$ git clone -b branch-3.5.6 https://github.com/apache/zookeeper.git`
2. `Cloning into 'zookeeper'...`
3. `remote: Enumerating objects: 1, done.`
4. `remote: Counting objects: 100% (1/1), done.`
5. `remote: Total59332(delta 0), reused 0(delta 0), pack-reused 59331`
6. `Receiving objects: 100% (59332/59332), 113.97MiB| 41.00KiB/s, done.`
7. `Resolving deltas: 100% (38347/38347), done.`
/<code>
3.進入剛才下載的zk源碼目錄,啟動cmd窗口,輸入編譯命令
<code>
1. `G:\\開發軟件>cd zk-3.5.6`
2. `G:\\開發軟件\\zk-3.5.6>cd apache-zookeeper-3.5.6`
3. `G:\\開發軟件\\zk-3.5.6\\apache-zookeeper-3.5.6>ant eclipse`
/<code>
然後等待最終編譯成功輸出,如果為 build successful字樣則為成功:
接下來,我們將代碼導入開發工具(官方使用的是eclipse,這裡我們使用ieda),
一路next,最後將maven編譯完成,即可完成源碼的編譯,我們可以看到源碼的目錄如下:
而加載完畢後,我們可以看到,zk3.5版本起,官方將模塊分離出來了,多出了很多的子模塊,比如文檔說明相關在 zookeeper-docs工程下,而zk的一些基礎數據結構類定義相關則在 zookeeper-jute工程下,而zk的客戶端和服務端相關的代碼則是分別在zookeeper-client與 zookeeper-server工程下,而在server工程下我們可以找到兩個啟動類: ZooKeeperServerMain以及 QuorumPeerMain,分別位於org.apache.zookeeper.server和org.apache.zookeeper.server.quorum包下,其中單機版的zk使用 ZooKeeperServerMain作為啟動類,集群版的zk使用 QuorumPeerMain啟動類。現在我們嘗試啟動單機版的zk,運行 ZooKeeperServerMain類,結果如下:
<code>
1. `錯誤: 找不到或無法加載主類 org.apache.zookeeper.server.ZooKeeperServerMain`
/<code>
這是怎麼回事呢?難道是jdk版本不對?但是我們查閱了官網,zk3.5.x版本對應的jdk的確是1.8,那麼只有一種可能就是編譯出來的class不是當前jdk編譯的,我們這裡選擇將zookeeper-server的工程重新編譯一下:
卻看到compile的時候控制檯報錯了,如下:
<code>
1. `[ERROR] Failed to execute goal org.codehaus.mojo:properties-maven-plugin:1.0.0:read-project-properties (default) on project zookeeper: Properties could not be loaded from File: D:\\360安全瀏覽器下載\\zk-3.5.6- 副本\\zookeeper-server\\src\\main\\resources\\git.properties -> [Help1]`
/<code>
看報錯的原因,似乎是有部分代碼使用了resources\\git.properties文件的配置,但是我們找了一下,在github上的代碼中不會存在git.properties,那麼是哪裡出問題了呢?我們來到pom中找到了 properties-maven-plugin插件,看到了如下的配置:
<code>1. `<plugin>`
2. `<groupid>org.codehaus.mojo/<groupid>`
3. `<artifactid>properties-maven-plugin/<artifactid>`
4. `<executions>`
5. `<execution>`
6. `<phase>initialize/<phase>`
7. `<goals>`
8. `<goal>read-project-properties/<goal>`
9. `/<goals>`
10. `<configuration>`
11. `<files>`
12. `<file>${basedir}/src/main/resources/git.properties/<file>`
13. `/<files>`
14. `/<configuration>`
15. `/<execution>`
16. `/<executions>`
17. `/<plugin>`
/<code>
原來是這裡初始化的時候會加載該配置文件的內容,除此之外我們還注意到一個插件exec-maven-plugin,裡面使用了部分參數在項目中並不存在,應該是引用了git.properties的配置參數導致了pom報錯,並且初始化編譯失敗,如下:
<code>
1. `<plugin>`
2. `<groupid>org.codehaus.mojo/<groupid>`
3. `<artifactid>exec-maven-plugin/<artifactid>`
4. `<executions>`
5. `<execution>`
6. `generate-version-info `
7. `<phase>generate-sources/<phase>`
8. `<goals>`
9. `<goal>exec/<goal>`
10. `/<goals>`
11. `<configuration>`
12. `<workingdirectory>${project.basedir}/src/main/java//<workingdirectory>`
13. `<executable>java/<executable>`
14. `<arguments>`
15. `<argument>-classpath/<argument>`
16. `<classpath>`
17. `<argument>org.apache.zookeeper.version.util.VerGen/<argument>`
18. `<argument>${project.version}/<argument>`
19. `<argument>${git.commit.id}/<argument>`
20. `<argument>${build.time}/<argument>`
21. `/<arguments>`
22. `/<configuration>`
23. `/<execution>`
24. `/<executions>`
25. `/<plugin>`
26. `<plugin>`
/<plugin>/<code>
並且我們也可以看到,這裡需要這些參數的原因是為了配置VerGen的版本、時間等參數,查閱官方github才知道,VerGen僅僅是為了開發迭代的時候區分每個人的版本不重複衝突使用的,我們個人運行和查閱源碼可以選擇固定一個值,所以接下來我們來將properties-maven-plugin插件註釋掉,並且將
{build.time}兩個參數改為自己的值,然後我們再次執行 mvn compile命令,此次編譯成功!再次運行main方法,發現控制檯出現了新的問題,輸入如下:
<code>
1. `log4j:WARN No appenders could be found for logger (org.apache.zookeeper.jmx.ManagedUtil).`
2. `log4j:WARN Please initialize the log4j system properly.`
3. `log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.`
4. `Usage: ZooKeeperServerMain configfile | port datadir [ticktime] [maxcnxns]`
/<code>
但是我們當前的啟動類沒有配置log4j,導致無法輸出對應的日誌信息,我們找到當前test包下的resources裡面的log4j.properties文件,在當前啟動類配置上對應的文件目錄(vm options):
<code>
1. `-Dlog4j.configuration=file:G:\\開發軟件\\zookeeper-release-3.5.6\\zookeeper-server\\src\\test\\resources\\log4j.properties`
/<code>
再次運行,發現啟動報錯了:
<code>
1. `2020-01-0701:28:48,727[myid:] - ERROR [main:ZooKeeperServerMain@66] - Invalid arguments, exiting abnormally`
2. `java.lang.IllegalArgumentException: Invalid number of arguments:[]`
3. `at org.apache.zookeeper.server.ServerConfig.parse(ServerConfig.java:60)`
4. `at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:103)`
5. `at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:64)`
6. `2020-01-0701:28:48,729[myid:] - INFO [main:ZooKeeperServerMain@67] - Usage: ZooKeeperServerMain configfile | port datadir [ticktime] [maxcnxns]`
/<code>
從報錯信息可以得出,我們需要一個運行時傳遞的參數,但是沒有配置,而這個參數就是我們啟動服務的時候需要修改的zoo.cfg文件,再次配置修改完畢(上面啟動用的zoo.cfg)的文件,如下:
這時我們再次運行,即可成功啟動zk的單機服務!
cli常用命令
zk服務端啟動以後,我們來開始學習zk客戶端命令,首先雙擊 zkCli.cmd,會默認進行本地的zk連接操作,如下:
help
我們先來看看zk有哪些命令操作,在cmd窗口中輸入 help命令回車,即可查看到zk當前支持的所有的命令,如下:
<code>
1. `[zk: localhost:2181(CONNECTED) 0] help`
2. `ZooKeeper-server host:port cmd args`
3. `stat path [watch]`
4. `set path data [version]`
5. `ls path [watch]`
6. `delquota [-n|-b] path`
7. `ls2 path [watch]`
8. `setAcl path acl`
9. `setquota -n|-b val path`
10. `history`
11. `redo cmdno`
12. `printwatches on|off`
13. `delete path [version]`
14. `sync path`
15. `listquota path`
16. `rmr path`
17. `get path [watch]`
18. `create [-s] [-e] path data acl`
19. `addauth scheme auth`
20. `quit`
21. `getAcl path`
22. `close`
23. `connect host:port`
/<code>
接下來我們來學習常見的一些命令操作。
ls 查看
如果我們想要查看當前的zk路徑下存在哪些文件,這個時候就需要使用到 ls系列的命令了,語法為 ls path[watch],我們輸入ls 以及對應的路徑即可查看,如下:
<code>
1. `[zk: localhost:2181(CONNECTED) 0] ls /`
2. `[zookeeper]`
/<code>
由於這裡我們沒有創建節點,所以輸出的結果為空
create -s -e path data acl 創建節點(-s:有順序的節點 -e:臨時的節點 acl:當前節點的權限)
我們知道zk裡有節點的概念,用來存儲數據,會綁定對應的路徑,這裡我們先來創建一個簡單的節點:
<code>
1. `[zk: localhost:2181(CONNECTED) 2] create /root root`
2. `Created/root`
/<code>
get path [watch] 獲取節點內容[可添加監聽]
創建節點命令成功了,這裡我們來通過get命令查看剛剛創建的節點吧,如下:
<code>
1. `[zk: localhost:2181(CONNECTED) 3] get /root`
2. `root`
3. `cZxid = 0x5`
4. `ctime = WedJan0122:30:10 CST 2020`
5. `mZxid = 0x5`
6. `mtime = WedJan0122:30:10 CST 2020`
7. `pZxid = 0x5`
8. `cversion = 0`
9. `dataVersion = 0`
10. `aclVersion = 0`
11. `ephemeralOwner = 0x0`
12. `dataLength = 4`
13. `numChildren = 0`
/<code>
可以看到當我們獲取/root節點的數據的時候,出現了一堆屬性,比如cZxid、ctime等,那麼這些屬性是什麼意思呢?彆著急,暫時留下一個懸念,後面我們會針對這些屬性進行詳細的講解。
stat path [watch] 節點更新[可添加監聽]
如果我們需要給某一個節點的內容進行更新操作,可以使用 stat命令對某個節點進行更新,如下:
<code>1. `[zk: localhost:2181(CONNECTED) 4] stat /root update_root`
2. `cZxid = 0x5`
3. `ctime = WedJan0122:30:10 CST 2020`
4. `mZxid = 0x5`
5. `mtime = WedJan0122:30:10 CST 2020`
6. `pZxid = 0x5`
7. `cversion = 0`
8. `dataVersion = 0`
9. `aclVersion = 0`
10. `ephemeralOwner = 0x0`
11. `dataLength = 4`
12. `numChildren = 0`
/<code>
ls2 path [watch] ls和stat的整合[可添加監聽]
還記得上面的命令有 ls,可以查看當前路徑下的節點,但是還有個ls2是幹啥的呢?其實ls2命令則是將ls與stat操作進行合併,實現了一個命令查看當前節點下的其他節點並且修改當前節點內容的操作,如下:
<code>
1. `[zk: localhost:2181(CONNECTED) 5] ls2 /root ls2_update_root`
2. `[]`
3. `cZxid = 0x5`
4. `ctime = WedJan0122:30:10 CST 2020`
5. `mZxid = 0x5`
6. `mtime = WedJan0122:30:10 CST 2020`
7. `pZxid = 0x5`
8. `cversion = 0`
9. `dataVersion = 0`
10. `aclVersion = 0`
11. `ephemeralOwner = 0x0`
12. `dataLength = 4`
13. `numChildren = 0`
/<code>
可以看到既返回了當前節點路徑下的所有節點集合,這裡因為沒有創建子節點,所以返回了[],並且我們設置的新的節點內容也生效並且返回了。
delete path [version] 刪除指定節點[可以指定版本號]
創建完節點以後,我們可以給對應路徑的節點進行刪除,並且需要注意的是,我們可以指定刪除的版本號,即可以實現樂觀鎖更新,防止誤操作,如下:
<code>
1. `[zk: localhost:2181(CONNECTED) 1] get /root`
2. `root`
3. `cZxid = 0x5`
4. `ctime = WedJan0122:30:10 CST 2020`
5. `mZxid = 0x5`
6. `mtime = WedJan0122:30:10 CST 2020`
7. `pZxid = 0x5`
8. `cversion = 0`
9. `dataVersion = 1`
10. `aclVersion = 0`
11. `ephemeralOwner = 0x0`
12. `dataLength = 4`
13. `numChildren = 0`
14. `[zk: localhost:2181(CONNECTED) 3] delete/root 1`
15. `[zk: localhost:2181(CONNECTED) 4] get /root`
16. `Node does not exist: /root`
/<code>
可以看到,我們查詢出的對應的dataVersion為1,所以當我們刪除的時候指定了版本1,再次查詢的時候報錯,即節點已經被刪除(Node does not exist: /root)。最後,今天的內容到這裡就結束了,感謝大家的收看!
後臺私信回覆【資料】即可獲取資料免費 領取方式哦!
閱讀更多 享學課堂online 的文章