Zookeeper是一個高可用的分佈式數據管理與協調框架,該框架能很好地保證分佈式環境中數據一致性。通常用來實現服務發現(相似DNS),配置管理,分佈式鎖,leader選舉等。html
1、生產環境中Zookeeper安裝部署規範java
生產環境建議zookeeper至少爲三臺集羣,統一安裝配置,版本號爲近期新版本,好比版本爲3.4.8
部署路徑:/opt/業務模塊名/zookeeper
配置文件:/opt/業務模塊名/zookeeper/conf/zoo.cfg
存儲快照文件snapshot的目錄:/opt/業務模塊名/zookeeper/data
事務日誌輸出目錄:/var/log/業務模塊名/zookeeper
運行日誌輸出目錄:/var/log/業務模塊名/zookeepernode
Zookeeper全部端口須要提早開通防火牆入站規則
對外服務端口:默認2181,可自定義
通訊端口:2888,可自定義
選舉端口:3888,可自定義
autoperge默認關閉,建議自行編寫腳本在業務低谷期清理快照和事務日誌程序員
查詢狀態:sh /opt/業務模塊名/zookeeper/zkServer.sh status
啓動服務:sh /opt/業務模塊名/zookeeper/zkServer.sh start
中止服務:sh /opt/業務模塊名/zookeeper/zkServer.sh stopredis
配置文件conf/zoo.cfg示例以下:算法
# The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/opt/kevintest/zookeeper_22181/data # the port at which the clients will connect clientPort=22181 # the maximum number of client connections. # increase this if you need to handle more clients #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature #autopurge.purgeInterval=1 dataLogDir=/var/log/kevintest/zookeeper_22181 server.1=192.168.10.91:22888:23888 server.2=192.168.10.91:22988:23988 server.3=192.168.10.92:22888:23888
conf/log4j.properties配置示例以下:數據庫
# Define some default values that can be overridden by system properties zookeeper.root.logger=INFO,ROLLINGFILE zookeeper.console.threshold=INFO zookeeper.log.dir=. zookeeper.log.file=zookeeper.log zookeeper.log.threshold=DEBUG zookeeper.tracelog.dir=. zookeeper.tracelog.file=zookeeper_trace.log # # ZooKeeper Logging Configuration # # Format is "<default threshold> (, <appender>)+ # DEFAULT: console appender only log4j.rootLogger=${zookeeper.root.logger} # Example with rolling log file #log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE # Example with rolling log file and tracing #log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE # # Log INFO level and above messages to the console # log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold} log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n # # Add ROLLINGFILE to rootLogger to get log file output # Log DEBUG level and above messages to a log file log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold} log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file} # Max log file size of 10MB #log4j.appender.ROLLINGFILE.MaxFileSize=10MB # uncomment the next line to limit number of backup files log4j.appender.ROLLINGFILE.MaxBackupIndex=10 log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n # # Add TRACEFILE to rootLogger to get log file output # Log DEBUG level and above messages to a log file log4j.appender.TRACEFILE=org.apache.log4j.FileAppender log4j.appender.TRACEFILE.Threshold=TRACE log4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.file} log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout ### Notice we are including log4j's NDC here (%x) log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n
conf/zookeeper-env.sh文件配置示例以下:apache
ZOO_LOG_DIR="/var/log/kevintest/zookeeper_22181/" ZOO_LOG4J_PROP="INFO,ROLLINGFILE
2、Zookeeper最佳實踐vim
必須瞭解如下ZK知識點,才能熟練地對ZK進行維護:
- zookeeper集羣裏分三種角色: Leader, Follower和Observer。Leader和Follower參與投票,Observer只會『聽』投票的結果,不參與投票。
- 投票集羣裏的節點數要求是奇數
- 一個集羣容忍掛掉的節點數的等式爲 N = 2F + 1,N爲投票集羣節點數,F爲能同時容忍失敗節點數。好比一個三節點集羣,能夠掛掉一個節點,5節點集羣能夠掛掉兩個...
- 一個寫操做須要半數以上的節點ack,因此集羣節點數越多,整個集羣能夠抗掛點的節點數越多(越可靠),可是吞吐量越差。
- Zookeeper裏全部節點以及節點的數據都會放在內存裏,造成一棵樹的數據結構。而且定時的dump snapshot到磁盤。
- Zookeeper的Client與Zookeeper之間維持的是長鏈接,而且保持心跳,Client會與Zookeeper之間協商出一個Session超時時間出來(其實就是Zookeeper Server裏配置了最小值,最大值,若是client的值在這兩個值之間則採用client的,小於最小值就是最小值,大於最大值就用最大值),若是在Session超時時間內沒有收到心跳,則該Session過時。
- Client能夠watch Zookeeper那個樹形數據結構裏的某個節點或數據,當有變化的時候會獲得通知。緩存
1)最小生產集羣
要確保Zookeeper可以穩定運行,那麼就須要確保投票可以正常進行,最好不要掛一個節點整個就不work了,因此咱們通常要求生產環境最少3個節點部署。
2)網絡
除了節點外,還要看不能一臺物理機器,一個機櫃或一個交換機掛掉而後影響了整個集羣,因此節點網絡結構也要考慮,這個可能就比不少應用服務器的要求更加嚴格。
3)分Group,保護核心Group
要確保Zookeeper整個集羣可靠運行,就是要確保投票集羣可靠。那在咱們這裏,將一個Zookeeper集羣劃分爲多個小的Group,咱們稱Leader+Follower爲核心Group,核心Group咱們通常是不向外提供服務的,而後咱們會根據不一樣的業務再加一些Observer,好比一個Zookeeper集羣爲服務發現,消息,定時任務三個不一樣的組件提供服務,那麼咱們爲創建三個Observer Group,分別給這三個組件使用,而Client只會鏈接分配給它的Observer Group,不去鏈接核心Group。這樣核心Group就不會給Client提供長鏈接服務,也不負責長鏈接的心跳,這大大的減輕了核心Group的壓力,由於在實際環境中,一個Zookeeper集羣要爲上萬臺機器提供服務,維持長鏈接和心跳仍是要消耗必定的資源的。由於Observer是不參與投票的因此加Observer並不會下降總體的吞吐量,並且Observer掛掉不會影響整個集羣的健康。
可是這裏要注意的是,分Observer Group只能解決部分問題,由於畢竟全部的寫入仍是要交給核心Group來處理的,因此對於寫入量特別大的應用來講,仍是須要進行集羣上的隔離,好比Storm和Kafka就對Zookeeper壓力比較大,你就不能將其與服務發現的集羣放在一塊兒。
4)內存
由於Zookeeper將全部數據都放在內存裏,因此對JVM以及機器的內存也要預先計劃,若是出現Swap那將嚴重的影響Zookeeper集羣的性能,因此我通常不怎麼推薦將Zookeeper用做通用的配置管理服務。由於通常配置數據仍是挺大的,這些所有放在內存裏不太可控。
5)日誌清理
由於Zookeeper要頻繁的寫txlog (Zookeeper寫的一種順序日誌) 以及按期dump內存snapshot到磁盤,這樣磁盤佔用就愈來愈大,因此Zookeeper提供了清理這些文件的機制,可是這種機制並不太合理,它只能設置間隔多久清理,而不能設置具體的時間段。那麼就有可能碰到高峯期間清理,因此建議將其關閉:autopurge.purgeInterval=0。而後使用crontab等機制,在業務低谷的時候清理。
6)日誌,jvm配置
從官網直接下載的包若是直接啓動運行是很糟糕的,這個包默認的配置日誌是不會輪轉的,並且是直接輸出到終端。咱們最開始並不瞭解這點,而後運行一段時間後發現生成一個龐大的zookeeper.out的日誌文件。除此以外,這個默認配置尚未設置任何jvm相關的參數(因此堆大小是個默認值),這也是不可取的。那麼有的同窗說那我去修改Zookeeper的啓動腳本吧。最好不要這樣作,Zookeeper會加載conf文件夾下一個名爲zookeeper-env.sh的腳本,因此你能夠將一些定製化的配置寫在這裏,而不是直接去修改Zookeeper自帶的腳本。
#!/usr/bin/env bash ZOO_LOG_DIR=/var/log/kevintest/zookeeper_22181/ #日誌文件放置的路徑 ZOO_LOG4J_PROP="INFO,ROLLINGFILE" #設置日誌輪轉
新版本的zk中是用java.env這個參數文件來配置的
if [ -f "$ZOOCFGDIR/java.env" ] then . "$ZOOCFGDIR/java.env" fi
其中$ZOOCFGDIR/java.env就是設置jvm內存大小的文件,這個文件默認狀況下是沒有的,須要手動建立,
vim /usr/local/services/zookeeper-3.4.8/conf/java.env
#!/bin/sh export JAVA_HOME=/usr/java/jdk # heap size MUST be modified according to cluster environment export JVMFLAGS="-Xms4096m -Xmx4096m $JVMFLAGS"
7)地址
在實際環境中,咱們可能由於各類緣由好比機器過保,硬件故障等須要遷移Zookeeper集羣,因此Zookeeper的地址是一個很頭痛的事情。這個地址有兩方面,第一個是提供給Client的地址,建議這個地址經過配置的方式下發,不要讓使用方直接使用,這一點咱們前期作的很差。另一個是集羣配置裏,集羣之間須要通信,也須要地址。咱們的處理方式是設置hosts:
192.168.1.20 zk1 192.168.1.21 zk2 192.168.1.22 zk3
在zoo.cfg配置裏:
server.1=zk1:2081:3801 server.2=zk2:2801:3801 server.3=zk3:2801:3801
這樣在須要遷移的時候,咱們停老的節點,起新的節點只須要修改hosts映射就能夠了。好比如今server.3須要遷移,那咱們在hosts裏將zk3映射到新的ip地址。可是對於java有一個問題是,java默認會永久緩存DNS cache,即便你將zk3映射到別的ip,若是並不重啓server.1, server.2,它是不會解析到新的ip的,這個須要修改$JAVA_HOME/jre/lib/security/java.security文件裏的networkaddress.cache.ttl=60,將其修改成一個比較小的數。對於這個遷移的問題,咱們還遇到一個比較尷尬的狀況,在最後的坑裏會有說起。
8)日誌位置
Zookeeper主要產生三種IO: txlog(每一個寫操做,包括新Session都會記錄一條log),Snapshot以及運行的應用日誌。通常建議將這三個IO分散到三個不一樣的盤上。不過咱們卻是一直沒有這麼實驗過,咱們的Zookeeper也是運行在虛擬機(通常認爲虛擬機IO較差)上。
9)監控
咱們對Zookeeper作了這樣一些監控:
a)是否可寫。 就是一個定時任務定時的去建立節點,刪節點等操做。這裏要注意的是Zookeeper是一個集羣,咱們監控的時候我仍是但願對單個節點作監控,因此這些操做的時候不要鏈接整個集羣,而是直接去鏈接單個節點。
b)監控watcher數和鏈接數 特別是這兩個數據有較大波動的時候,能夠發現使用方是否有誤用的狀況
c)網絡流量以及client ip 這個會記錄到監控系統裏,這樣很快能發現『害羣之馬』
10)一些使用建議
a)不要強依賴Zookeeper,也就是Zookeeper出現問題業務已然能夠正常運行。Zookeeper是一個分佈式的協調框架,主要作的事情就是分佈式環境的一致性。這是一個很是苛刻的事情,因此它的穩定性受不少方面的影響。好比咱們經常使用Zookeeper作服務發現,那麼服務發現實際上是不須要嚴格的一致性的,咱們能夠緩存server list,當Zookeeper出現問題的時候已然能夠正常工做,在這方面etcd要作的更好一些,Zookeeper若是出現分區,少數派是不能提供任何服務的,讀都不能夠,而etcd的少數派仍然能夠提供讀服務,這在服務發現的時候仍是不錯的。
b)不要將不少東西塞到Zookeeper裏,這個上面已經提到過。
c)不要使用Zookeeper作細粒度鎖,好比不少業務在訂單這個粒度上使用Zookeeper作分佈式鎖,這會頻繁的和Zookeeper交互,對Zookeeper壓力較大,並且一旦出現問題影響面廣。可是可使用粗粒度的鎖(其實leader選舉也是一種鎖)。
d)不建議作通用配置的第二個理由是,通用配置要提供給特別多特別多系統使用,並且一些公共配置甚至全部系統都會使用,一旦這樣的配置發生變動,Zookeeper會廣播給全部的watcher,而後全部Client都來拉取,瞬間形成很是大的網絡流量,引發所謂的『驚羣』。而本身實現通用配置系統的時候,通常會對這種配置採起排隊或分批通知的方式。
3、Zookeeper操做命令手冊
1)Zookeeper客戶端命令
zkCli.sh -server 127.0.0.1:2181 [zk: localhost:2182(CONNECTED) 0] help ZooKeeper -server host:port cmd args connect host:port get path [watch] ls path [watch] set path data [version] rmr path delquota [-n|-b] path quit printwatches on|off create [-s] [-e] path data acl stat path [watch] close ls2 path [watch] history listquota path setAcl path acl getAcl path sync path redo cmdno addauth scheme auth delete path [version] setquota -n|-b val path
登陸後命令行裏面的一些簡單操做以下:
- 顯示根目錄下、文件: ls / #使用 ls 命令來查看當前 ZooKeeper 中所包含的內容
- 顯示根目錄下、文件: ls2 / #查看當前節點數據並能看到更新次數等數據
- 建立文件,並設置初始內容: create /zk "test" #建立一個新的 znode節點「 zk 」以及與它關聯的字符串
- 獲取文件內容: get /zk #確認 znode 是否包含咱們所建立的字符串
- 修改文件內容: set /zk "zkbak" #對 zk 所關聯的字符串進行設置
- 刪除文件: delete /zk #將剛纔建立的 znode 刪除
- 退出客戶端: quit
- 幫助命令: help
2)Zookeeper服務端命令
在準備好相應的配置以後,能夠直接經過zkServer.sh 這個腳本進行服務的相關操做
- 啓動ZK服務: sh bin/zkServer.sh start
- 查看ZK服務狀態: sh bin/zkServer.sh status
- 中止ZK服務: sh bin/zkServer.sh stop
- 重啓ZK服務: sh bin/zkServer.sh restart
3)Zookeeper經常使用四字命令
ZooKeeper 支持某些特定的四字命令字母與其的交互。它們大可能是查詢命令,用來獲取 ZooKeeper 服務的當前狀態及相關信息。用戶在客戶端能夠經過telnet 或 nc 向 ZooKeeper 提交相應的命令。命令語義:
命令操做實例:(機器上要安裝netcat-0.7.1-1.i386.rpm服務)
# echo stat |nc 127.0.0.1 2182 來查看哪一個節點被選擇做爲follower或者leader # echo ruok |nc 127.0.0.1 2182 測試是否啓動了該Server,若回覆imok表示已經啓動。 # echo dump | nc 127.0.0.1 2182 列出未經處理的會話和臨時節點。 # echo kill | nc 127.0.0.1 2182 關掉server # echo conf | nc 127.0.0.1 2182 輸出相關服務配置的詳細信息。 # echo cons | nc 127.0.0.1 2183 列出全部鏈接到服務器的客戶端的徹底的鏈接 / 會話的詳細信息。 # echo envi | nc 127.0.0.1 2182 輸出關於服務環境的詳細信息(區別於 conf 命令)。 # echo reqs | nc 127.0.0.1 2183 列出未經處理的請求。 # echo wchs | nc 127.0.0.1 2183 列出服務器 watch 的詳細信息。 # echo wchc | nc 127.0.0.1 2183 經過 session 列出服務器 watch 的詳細信息,它的輸出是一個與 watch 相關的會話的列表。 # echo wchp | nc 127.0.0.1 2183 經過路徑列出服務器 watch 的詳細信息。它輸出一個與 session 相關的路徑。
4、Zookeeper運維手冊
對於長期運行的ZooKeeper ensemble來講, 運維工做是必須作的, 運維人員須要注意如下幾點:
1)清理磁盤文件
ZooKeeper中有兩處使用到了磁盤:事務日誌與內存數據庫快照.。ZooKeeper名稱空間裏的節點發生變動的時候, 就會有內容寫入事務日誌. 一般狀況下, 當單個事務日誌文件變的愈來愈大的時候, 事務日誌就須要建立一個新的文件. 但在建立新的事務日誌文件以前, ZooKeeper會先把當前的內存數據庫的狀態寫入磁盤先作快照, 而後再生成一個新的事務日誌文件. 這樣就保證了快照文件和事務日誌文件是一一對應的. 但快照落地須要時間, 在快照落地期間若是還有事務來臨, 那麼這部分事務的日誌依然會寫向舊的事務日誌文件裏. 這就致使, 快照文件對應的那個事務日誌文件裏, 存儲的事務日誌可能要比當前快照文件要新.
ZooKeeper server進程在默認啓動的狀況下, 是不會自動刪除事務日誌文件和快照文件的,固然這是可配置的, 配置項分別是 autopurge.snapRetainCount 和 autopurge.purgeInterval. 這兩個配置項的具體含義在有詳細描述. 但須要注意:若是你要這樣作, 那麼最好爲每臺部署的機器提供不一樣的配置值, 除非這些機器的規格是徹底一摸同樣的!
除過在配置文件中設定, 還有一種方法就是調用一個ZooKeeper提供的小工具, 大體以下:
java -cp zookeeper.jar:lib/log4j-1.2.15.jar:conf org.apache.zookeeper.server.PurgeTxnLog <dataDir> <snapDir> -n <count>
其中<dataDir>
是事務日誌的保存目錄,<snapDir>
是快照文件的保存目錄,<count>
是要保留的個數. 建議大於3.。運行該命令後, 除了最近的<count>
對事務日誌文件與快照文件, 其它文件都將被刪除. 這是一個一次性命令. 若是你想按期清理, 那麼只能本身寫個腳本咯。
注意如下幾點:
- 永遠不建議手動刪除事務日誌文件與快照文件;
- 經過配置項使ZooKeeper server自動刪除, 只有在ZooKeeper版本大於3.4後纔可用;
- PrugeTxnLog工具是一個一次性工具, 若是須要按期清理, 你須要本身寫一個腳本;
- 當機器規格不一樣的時候, 建議按照不一樣規格定製不一樣的清楚閾值;
2)清理運行日誌
ZooKeeper 用 log4j 來輸出運行日誌。若是要更改運行日誌的相關配置,你須要獨立爲log4j提供配置文件。建議使用log4j提供的滾動日誌特性,這樣就免去了清理運行日誌的問題。
3)監控ZooKeeper server進程的死活
ZooKeeper的server進程在錯誤發生的時候會當即自殺,ZooKeeper的設計哲學是這樣的:
- 單個實例掛掉, 或少許實例掛掉不影響總體服務
- 當單個實例遇到錯誤的時候, 實例會當即掛掉
- 實例被重啓後會自動加入ensemble
- 但實例不會自動重啓
因此搞一個監控進程, 在實例進程掛掉以後將其當即拉起是一個很好的作法. 好比daemontools或SMF.
4)監控ZooKeeper server服務的狀態
要監控ZooKeeper服務的狀態, 有兩個選擇
- 用4字命令去檢查。這個在上面的 ZooKeeper4字命令中有詳情
- JMX。
5)運行日誌
ZooKeeper使用log4j 1.2來輸出運行日誌,默認的配置文件在zookeeper/conf/log4j.properties中。log4j的配置文件要求要麼放在工做目錄裏, 要麼放在類路徑裏。
6)問題定位
[ 因爲文件損壞致使實例不能啓動 ]
ZooKeeper的server進程在事務日誌文件被損壞的狀況下是起不來的。這時運行日誌會說在載入ZooKeeper database時出現IOException。這種狀況下,你須要作的是:
- 經過四字命令stat檢查ensemble中的其它實例是否正常工做
- 若是其它實例正常, 那麼把當前實例dataDir目錄下的version-2子目錄中的全部文件刪除, 再把dataLogDir下的version-2子目錄下的全部文件刪除, 而後重啓就能夠了。
這種狀況是當前實例的事務文件損壞, 不能重建內存數據庫, 刪除掉事務日誌和數據庫快照後, 當前的實例在重啓後會經過其它實例拉取內在數據庫, 重建事務日誌和快照文件.
7)配置參數
ZooKeeper的行爲受配置文件影響. 全部同一個ensemble中的實例建議使用徹底相同的配置文件. 但使用徹底相同的配置文件有一個前提條件: 就是全部實例所屬的機器上的磁盤佈局是相同的. 磁盤佈局不一樣意味着不一樣的機器下的實例在配置dataDir和dataLogDir的時候配置值可能有差別, 但除此以外, 一個ensemble中的全部實例的配置文件必須保證server.x=xxxx這些配置值是徹底一致的。
7.1)最小配置
下面列出來的是要讓ensemble正常工做, 每一個實例都須要配置的配置項。配置項含義:
7.2)高級可選配置
下面列出來的是一此可選配置, 屬於高級選項. 你能夠用這些配置項進一步個性化ZooKeeper server的行爲. 其中一些配置項的值能夠經過在啓動server進程的時候寫入Java 系統屬性來設置。配置項對應的Java系統屬性名含義:
7.3)多實例模式下的配置項
下面列出來的配置項是多實例模式下的一些配置項. 有一些配置項能夠經過在啓動server進程的時候寫入Java系統屬性來設置。配置項對應的Java系統屬性名含義
7.4)身份認證與受權相關的配置項
爲了不看不懂下面的配置項都在幹嗎,先大體說一下Zookeeper裏的認證與受權。在ZooKeeper server端, 每一個znode存儲兩部份內容: 數據和狀態. 狀態中包含ACL信息. 建立一個znode會產生一個ACL表, 每一個ACL記錄有如下內容:
- 驗證模式(scheme)
- 具體內容(id). 好比當scheme=="digest"的時候, id爲是用戶名和密碼, 好比"root:J0sTy9BCUKubtK1y8pkbL7qoxSw="
- 這個ACL擁有的權限
ZooKeeper提供了以下幾種驗證模式(scheme)
- digest 就是用戶名+密碼.
- auth 不使用任何id, 表示任何已確認用戶
- ip. 用client鏈接至server時使用的IP地址進行驗證
- world 固定ID爲"anyone", 爲全部client端開放權限
- super 在這種scheme下, 對應的id擁有超級權限.
須要注意:exists操做的getAcl操做不受ACL控制, 任何client均可以執行這兩個操做.
znode的權限主要有如下幾種:
- create
- read
- write
- delete
- admin 容許對本節點執行setAcl操做
配置項對應的Java系統屬性名含義:
7.5)實驗性的配置項
配置項對應的Java系統屬性名含義
7.6)不安全的配置項
配置項對應的Java系統屬性名含義
7.7)使用Netty框架進行通訊
這是3.4版本後的一個特性。Netty是一個基於NIO的客戶端-服務器通訊框架, 這個框架簡化了Java在網絡通訊層上的不少繁操做, 而且內置支持SSL和認證受權, 固然SSL和認證受權是額外的可選功能.
3.4版本以前,ZooKeeper是直接用NIO的,在3.4以後,NIO只是一個可選項,但依然是默認選項,若是要使用Netty的話,須要把zookeeper.serverCnxnFactory替換爲org.apache.zookeeper.server.NettyServerCnxnFactory。能夠只在client上用Netty,也可在server上用Netty,但一般狀況下,建議要改一塊兒改。蛋疼的是相關的文檔官方尚未寫!
7.8)四字命令
ZooKeeper支持一系列的四字命令, 你能夠在client上經過telnte或nc直接向server發送這些四字命令.使用一個四字命令以下所示, 下面使用echo和nc將四字命令ruok發送給本機的server:
echo ruok | nc 127.0.0.1 2182
下表是全部支持的四字命令, 注意有些命令僅在特定版本以後才受支持。命令含義:
這裏須要注意:
mntr命令的輸出大體長下面這樣. 輸出格式符合java屬性格式, 若是你要寫個腳本定時發送這個命令以監控ensemble的運行狀態, 注意輸出的字段的數量可能會有變化, 寫腳本的時候注意這一點。另外有一些字段是與操做系統平臺相關的, 輸出每一行的格式是key \t value, 下面是一個示例:
# echo mntr | nc localhost 2185 zk_version 3.4.0 zk_avg_latency 0 zk_max_latency 0 zk_min_latency 0 zk_packets_received 70 zk_packets_sent 69 zk_outstanding_requests 0 zk_server_state leader zk_znode_count 4 zk_watch_count 0 zk_ephemerals_count 0 zk_approximate_data_size 27 zk_followers 4 - only exposed by the Leader zk_synced_followers 4 - only exposed by the Leader zk_pending_syncs 0 - only exposed by the Leader zk_open_file_descriptor_count 23 - only available on Unix platforms zk_max_file_descriptor_count 1024 - only available on Unix platforms
8)數據文件管理
將事務日誌文件和快照文件存儲在不一樣的物理磁盤上, 能夠提高系統性能.
8.1)快照存儲目錄
配置項dataDir指向的目錄路徑中主要存儲兩種文件:
- myid: 這個文件裏寫着當前server實例的編號
- snapshot.<zxid>: 這裏存儲着內存數據庫的快照
server實例的編號用在兩個場合:myid文件裏, 以及配置文件裏的server.X配置項中。當前server實例在啓動的時候, 先去配置文件裏看dataDir的值, 而後去找dataDir/myid這個文件, 查看文件內容, 得知本身的編號, 而後在配置文件裏再找對應的server.x查看要開的端口號。
快照文件的後綴, <zxid>, 是一個事務ID. 這是在落地內存數據庫這個過程開始時, 成功執行的最後一個事務的ID號, 但蛋疼的是, 在落地快照的過程當中, server還在接受請求, 執行事務, 也就是在落地的過程當中, 內存數據庫中的數據還處於一個變更的過程當中, 這就致使落地後的快照文件像是一個扭曲的文件. 像是你在用手機拍攝全景照片的過程當中, 有一隻貓隨着你的鏡頭走, 而後最終拍攝出來的照片裏有一隻長度爲17米的貓. 最終落地生成的快照文件裏的數據狀態可能和任何一個時刻內存數據庫的狀態都對不上, 就是由於這個緣由. 但ZooKeeper依然能夠用這種扭曲的快照文件重建內存數據庫, 這是由於ZooKeeper中的update操做是冪等的, 這就保證了在扭曲的快照文件之上重放事務日誌裏的日誌, 就能夠將進程的內存狀態恢復到日誌結束時的那個時刻。
8.2)事務日誌目錄
在有update請求的時候, server的默認行爲是先寫事務日誌, 再執行update操做. 單個事務日誌裏存儲的事務個數超過一個閾值的時候, 就會致使事務日誌新開一個文件, 同時會致使內在數據庫落地快照, 這個閾值在上面的配置項中有提. 日誌文件的後綴是日誌文件裏第一個日誌的ID
8.3)文件管理
快照文件的格式和事務日誌文件的格式是死的,這就容許你從現網的server機器上將事務日誌和內存快照拷貝至你的開發機,在你的開發環境重現現網的情景, 從而進行一些調試或問題定位操做。
使用舊的事務日誌文件和快照文件還能重建過去某個指定時刻server的狀態, LogFormatter類能夠用來訪問事務日誌文件, 以獲取可讀的信息. 固然使用的時候須要有管理員權限, 由於數據是加密的。
server進程原本是沒有刪除事務日誌和快照文件的能力的, 但這在3.4版本中也隨着新的配置項autopurge.snapRetainCount和autopurge.purgeInterval添加上了。
9)要避免的事情
下面是幾個你應當在部署運維的時候極力避免的事情:
- ensemble中各個server使用的配置文件中,server.X配置表不一致. 全部的配置文件中, 都要以server.X配置項的形式列出當前ensemble中的全部server, 包括本身. 若是這個東西不一致, 會炸。
- 事務日誌目錄設置不合理。將事務日誌目錄指向一個IO繁忙的磁盤, 會致使server始終處於一個半死不活的狀態;
- 不正確的java heap size。頻繁的swap操做會嚴重拖慢性能. 保守起見, 若是你的機器有4G內存, 把java heap size設置爲3G就行了;
- 部署的時候不考慮安全性。建議在生產環境中合理配置防火牆;
5、Zookeeper常見問題彙總
1)zookeeper client 3.4.5 ping時間間隔算法有問題,在遇到網絡抖動等緣由致使一次ping失敗後會斷開鏈接。3.4.6解決了這個問題 Bug1751。
2)zookeeper client若是由於網絡抖動斷開了鏈接,若是後來又重連上了,zookeeper client會自動的將以前訂閱的watcher等又所有訂閱一遍,而Zookeeper默認對單個數據包的大小有個1M的限制,這每每就會超限,最後致使一直不斷地的重試。這個問題在較新的版本獲得了修復。Bug706
3)拋出UnresolvedAddressException異常致使Zookeeper選舉線程退出,整個集羣沒法再選舉,處於崩潰的邊緣。這個問題是,某次OPS遷移機器,將老的機器回收了,因此老的機器的IP和機器名不復存在,最後拋出UnresolvedAddressException這個異常,而Zookeeper的選舉線程(QuorumCnxManager類裏的Listener)只捕獲了IOException,致使該線程退出,該線程一旦退出只要如今的leader出現問題,須要從新選舉,則不會選出新的leader來,整個集羣就會崩潰。Bug2319
4)因爲文件損壞致使實例不能啓動
ZooKeeper的server進程在事務日誌文件被損壞的狀況下是起不來的. 這時運行日誌會說在載入ZooKeeper database時出現IOException. 這種狀況下, 你須要作的是:
- 經過四字命令stat檢查ensemble中的其它實例是否正常工做
- 若是其它實例正常, 那麼把當前實例dataDir目錄下的version-2子目錄中的全部文件刪除, 再把dataLogDir下的version-2子目錄下的全部文件刪除, 而後重啓就能夠了.
這種狀況是當前實例的事務文件損壞, 不能重建內存數據庫, 刪除掉事務日誌和數據庫快照後, 當前的實例在重啓後會經過其它實例拉取內在數據庫, 重建事務日誌和快照文件.
6、Zookeeper中的Znode特性說明
在ZooKeeper中,節點也稱爲znode。因爲對於程序員來講,對zookeeper的操做主要是對znode的操做,所以,有必要對znode進行深刻的瞭解。 ZooKeeper採用了相似文件系統的的數據模型,其節點構成了一個具備層級關係的樹狀結構。Zookeeper擁有一個命名空間就像一個精簡的文件系統,不一樣的是它的命名空間中的每一個節點擁有它本身或者它下面子節點相關聯的數據。Zookeeper中必須使用絕對路徑也就是使用「/」開頭。例如,下圖展現了zookeeper節點的層級樹狀結構。
圖中,根節點 / 包含了兩個字節點 /module1,/module2,而節點 /module1 又包含了三個字節點 /module1/app1,/module1/app2,/module1/app3。在zookeeper中,節點以絕對路徑表示,不存在相對路徑,且路徑最後不能以 / 結尾(根節點除外)。
ZooKeeper以一種相似於文件系統的樹形數據結構實現名稱空間。名稱空間中的每一個節點都是一個znode。znode和文件系統的路徑不同,在文件系統中,路徑只是一個名稱,不包含數據。而znode不只是一個路徑,還攜帶數據。
此外,znode還維護了包括版本號和時間戳的狀態信息。經過版本號和時間戳信息,可讓ZooKeeper驗證緩存、協調每次的更改操做。每當znode數據發生更改時,版本號都會遞增。客戶端檢索znode時,同時也會收到關於該節點的狀態信息。當客戶端執行更改、刪除操做時,它必須提供它正在更改的znode數據的版本,若是它提供的版本與數據的實際版本不匹配,則更新將失敗。
Zookeeper目錄樹中每一個節點對應一個Znode。每一個Znode維護這一個屬性,當前版本、數據版本、創建時間和修改時間等,看下圖:
Zookeeper就是使用這些屬性來實現特殊功能的。當一個客戶端要對某個節點進行修改時,必須提供該數據的版本號,當節點數據發生變化是其版本號就會增長。以下圖:
0)Znode節點特性
-> Watches:客戶端能夠在節點上設置Watches(能夠叫作監視器)。當節點狀態發生變化時,就會觸發監視器對應的操做,當監視器被觸發時,zookeeper服務器會向客戶端發送且只發送一個通知
-> 數據訪問:zookeeper上存儲的數據須要被原子性的操做(要麼修改爲功要麼回到原樣),也是就讀操做將會讀取節點相關全部數據,寫操做也會修改節點相關全部數據,,並且每一個節點都有本身的ACL。
節點類型:zookeeper中有幾種節點類型,節點類型在節點建立的時候就被肯定且不可改變
-> 臨時節點(EPHEMERAL):臨時建立的,會話結束節點自動被刪除,也能夠手動刪除,臨時節點不能擁有子節點
-> 臨時順序節點(EPHEMERAL_SEQUENTIAL):具備臨時節點特徵,可是它會有序列號,分佈式鎖中會用到該類型節點
-> 持久節點(PERSISTENT):建立後永久存在,除非主動刪除。
-> 持久順序節點(PERSISTENT_SEQUENTIAL):該節點建立後持久存在,相對於持久節點它會在節點名稱後面自動增長一個10位數字的序列號,這個計數對於此節點的父節點是惟一,若是這個序列號大於2^32-1就會溢出。
1)znode節點類型
根據節點的存活時間,能夠對節點劃分爲持久節點和臨時節點。節點的類型在建立時就被肯定下來,而且不能改變。
- 持久節點的存活時間不依賴於客戶端會話,只有客戶端在顯式執行刪除節點操做時,節點才消失。
- 臨時節點的存活時間依賴於客戶端會話,當會話結束,臨時節點將會被自動刪除(固然也能夠手動刪除臨時節點)。利用臨時節點的這一特性,咱們可使用臨時節點來進行集羣管理,包括髮現服務的上下線等。 ZooKeeper規定,臨時節點不能擁有子節點。
持久節點
使用命令create能夠建立一個持久節點
create /module1 module1
這樣,便建立了一個持久節點/module1,且其數據爲"module1"。
臨時節點
使用create命令,並加上-e參數,能夠建立一個臨時節點
create -e /module1/app1 app1
這樣,便建立了一個臨時節點 /module1/app1,數據爲"app1"。關閉會話,而後輸入命令
get /module1/app1
能夠看到有如下提示,說明臨時節點已經被刪除
Node does not exist: /module1/app1
順序節點
ZooKeeper中還提供了一種順序節點的節點類型。每次建立順序節點時,zk都會在路徑後面自動添加上10位的數字(計數器),例如 < path >0000000001,< path >0000000002,……這個計數器能夠保證在同一個父節點下是惟一的。在zk內部使用了4個字節的有符號整形來表示這個計數器,也就是說當計數器的大小超過2147483647時,將會發生溢出。
順序節點爲節點的一種特性,也就是,持久節點和臨時節點均可以設置爲順序節點。這樣一來,znode一共有4種類型:持久的、臨時的,持久順序的,臨時順序的。
使用命令create加上-s參數,能夠建立順序節點(-e參數爲建立臨時節點,若是不帶參數則建立持久節點)。例如
create -s /module1/app app
輸出
Created /module1/app0000000001
便建立了一個持久順序節點 /module1/app0000000001。若是再執行此命令,則會生成節點 /module1/app0000000002。
若是在create -s再添加-e參數,則能夠建立一個臨時順序節點。以下示例:
2)znode節點數據
在建立節點時能夠指定節點中存儲的數據。ZooKeeper保證讀和寫都是原子操做,且每次讀寫操做都是對數據的完整讀取或完整寫入,並不提供對數據進行部分讀取或者寫入操做。
如下命令建立一個節點/module1/app2,且其存儲的數據爲app2。
create /module1/app2 app2
ZooKeeper雖然提供了在節點存儲數據的功能,但它並不將本身定位爲一個通用的數據庫,也就是說,你不該該在節點存儲過多的數據。Zk規定節點的數據大小不能超過1M,但實際上咱們在znode的數據量應該儘量小,由於數據過大會致使zk的性能明顯降低。若是確實須要存儲大量的數據,通常解決方法是在另外的分佈式數據庫(例如redis)中保存這部分數據,而後在znode中咱們只保留這個數據庫中保存位置的索引便可。
3)znode節點屬性
每一個znode都包含了一系列的屬性,經過命令get就能夠得到節點的屬性。
get /module1/app2 app2 cZxid = 0x20000000e ctime = Thu Jun 30 20:41:55 HKT 2016 mZxid = 0x20000000e mtime = Thu Jun 30 20:41:55 HKT 2016 pZxid = 0x20000000e cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 4 numChildren = 0
4)zookeeper中的版本號
version含義:對節點的每次操做都會使節點的版本號增長,有三個版本號dataversion(數據版本號)、cversion(子節點版本號)、aclversion(節點所擁有的ACL版本號)。
對於每一個znode來講,均存在三個版本號:
- dataVersion
數據版本號,每次對節點進行set操做,dataVersion的值都會增長1(即便設置的是相同的數據)。
- cversion
子節點的版本號。當znode的子節點有變化時,cversion 的值就會增長1。
- aclVersion
ACL的版本號,關於znode的ACL(Access Control List,訪問控制),能夠參考 參考資料1 有關ACL的描述。
以數據版本號來講明zk中版本號的做用。每個znode都有一個數據版本號,它隨着每次數據變化而自增。ZooKeeper提供的一些API例如setData和delete根據版本號有條件地執行。多個客戶端對同一個znode進行操做時,版本號的使用就會顯得尤其重要。例如,假設客戶端C1對znode /config寫入一些配置信息,若是另外一個客戶端C2同時更新了這個znode,此時C1的版本號已通過期,C1調用setData必定不會成功。這正是版本機制有效避免了數據更新時出現的前後順序問題。在這個例子中,C1在寫入數據時使用的版本號沒法匹配,使得操做失敗。下圖描述了這個狀況。下面表示使用版本號來阻止並行操做的不一致性:
zookeeper中版本號的做用
Zookeeper裏面的版本號和咱們理解的版本號不一樣,它表示的是對數據節點的內容、子節點列表或者ACL信息的修改次數。節點建立時dataversion、aclversion,cversion都爲0,每次修改響應內容其對應的版本號加1。
這個版本號的用途就和分佈式場景的一個鎖概念有關。好比演出售票中的一個座位,顯然每一個場次中的每一個座位都只有一個,不可能賣出2次。若是A下單的時候顯示可售,他想買,那麼爲了保證他能夠下單成功,此時別人就不能買。這時候就須要有一種機制來保證同一時刻只能有一我的去修改該座位的庫存。這就用到了鎖,鎖有悲觀鎖和樂觀鎖。
- 悲觀鎖:它會假定全部不一樣事務的處理必定會出現干擾,數據庫中最嚴格的併發控制策略,若是一個事務A正在對數據處理,那麼在整個事務過程當中,其餘事務都沒法對這個數據進行更新操做,直到A事務釋放了這個鎖。
- 樂觀鎖:它假定全部不一樣事務的處理不必定會出現干擾,因此在大部分操做裏不準加鎖,可是既然是併發就有出現干擾的可能,如何解決衝突就是一個問題。在樂觀鎖中當你在提交更新請求以前,你要先去檢查你讀取這個數據以後該數據是否發生了變化,若是有那麼你這次的提交就要放棄,若是沒有就能夠提交。
Zookeeper中的版本號就是樂觀鎖,你修改節點數據以前會讀取這個數據並記錄該數據版本號,當你須要更新時會攜帶這個版本號去提交,若是你此時攜帶的版本號(就是你上次讀取出來的)和當前節點的版本號相同則說明該數據沒有被修改過,那麼你的提交就會成功,若是提交失敗說明該數據在你讀取以後和提交以前這段時間內被修改了。
這裏經過set命令並攜帶版本號提交更新,版本號相同更新就會成功。
若是你再次更新並使用以前的版本號那麼就會失敗。
5)zookeeper中的事務ID
對於zookeeper來講,每次的變化都會產生一個惟一的事務id,zxid(ZooKeeper Transaction Id)。經過zxid,能夠肯定更新操做的前後順序。例如,若是zxid1小於zxid2,說明zxid1操做先於zxid2發生。須要指出的是,zxid對於整個zookeeper都是惟一的,即便操做的是不一樣的znode。
- cZxid 即Znode建立的事務id。
- mZxid 即Znode被修改的事務id,即每次對znode的修改都會更新mZxid。
下面表示Zxid在客戶端重連中的做用:
在集羣模式下,客戶端有多個服務器能夠鏈接,當嘗試鏈接到一個不一樣的服務器時,這個服務器的狀態要與最後鏈接的服務器的狀態要保持一致。Zk正是使用zxid來標識這個狀態,圖3描述了客戶端在重連狀況下zxid的做用。當客戶端因超時與S1斷開鏈接後,客戶端開始嘗試鏈接S2,但S2延遲於客戶端所識別的狀態。然而,S3的狀態與客戶端所識別的狀態一致,因此客戶端能夠安全鏈接上S3。
ZXID含義:ZooKeeper節點狀態改變會致使該節點收到一個zxid格式的時間戳,這個時間戳是全局有序的,每次更新都會產生一個新的。若是zxid1的值小於zxid2,那麼說明zxid2發生的改變在zxid1以後。zxid是一個惟一的事務ID,具備遞增性,一個znode的創建或者更新都會產生一個新的zxid值,具體時間有3個cZxid(節點建立時間)、mZxid(該節點修改時間,與子節點無關)、pZxid(該節點的子節點的最後一次建立或者修改時間,孫子節點無關)
下面對zookeeper的時間和版本號作一說明:
6)zookeeper中的時間戳
包括znode的建立時間和修改時間,建立時間是znode建立時的時間,建立後就不會改變;修改時間在每次更新znode時都會發生變化。如下命令建立了一個 /module2 節點
create /module2 module2 Created /module2
經過 get 命令,能夠看到 /module2的 ctime和mtime均爲Sat Jul 02 11:18:32 CST 2018
get /module2 module2 cZxid = 0x2 ctime = Sat Jul 02 11:18:32 CST 2018 mZxid = 0x2 mtime = Sat Jul 02 11:18:32 CST 2018 pZxid = 0x2 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 7 numChildren = 0
修改 /module2,能夠看到 ctime 沒有發生變化,mtime已更新爲最新的時間
set /module2 module2_1 cZxid = 0x2 ctime = Sat Jul 02 11:18:32 CST 2018 mZxid = 0x3 mtime = Sat Jul 02 11:18:50 CST 2018 pZxid = 0x2 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 9 numChildren = 0
7)znode須要關注的點:
- Watches
客戶端可在znode上設置watchs。每當該znode發生改變時,就會觸發設置在這個znode上的watch。當觸發了watch,ZooKeeper會發送一個通知給客戶端。
- Data Access
在每一個znode名稱空間中存儲的數據的讀、寫操做都是原子性的。讀操做將獲取與znode關聯的全部數據(包括數據的狀態信息),寫操做將替換該znode所攜帶的全部數據。每一個節點都有一個訪問控制列表(ACL)來限制誰能夠作什麼。
ZooKeeper並無被設計成通常的數據庫或大型對象存儲。相反,它只是管理協調數據。這些數據能夠以配置、狀態信息等形式出現。各類形式的協調數據的一個共同特色是它們相對較小,通常以kb做爲度量度量。ZooKeeper客戶端和服務器實現都有完整的檢查功能,以確保znode的數據少於1M,通常來講,協調數據佔用的空間都遠遠小於1M。在相對較大的數據大小上操做會致使一些操做比其餘操做花費更多的時間,而且會影響一些操做的延遲,由於它要在網絡上傳輸更多數據。若是須要存儲較大數據,能夠將它們存儲在大型存儲系統(如NFS或HDFS)上,而後在ZooKeeper中使用指針指向這些較大數據。
- Ephemeral Nodes
ZooKeeper容許使用臨時(ephemeral)節點。只要建立臨時znode的會話還存在,臨時znode就存在。會話退出,這個會話上建立的臨時節點都會刪除。所以,臨時節點上不容許出現子節點。
- Sequence Nodes -- Unique Naming
建立znode時,還能夠請求ZooKeeper將單調遞增的計數器追加到znode路徑的末尾。這個計數器是父znode獨有的。計數器的格式爲%010d,即便用0來填充的10位數字(計數器以這種方式進行格式化以簡化排序),例如<path>0000000001。注意:用於存儲下一個序列號的計數器是由父節點維護的有符號整數(4bytes),當計數器的增量超過2147483647時,計數器將溢出。
8)ZooKeeper中的時間
- Zxid
每次更改ZooKeeper的狀態,都會設置到一個zxid(ZooKeeper的事務id)格式的版本戳。zxid暴露了ZooKeeper中全部更改操做的總順序。由於每次更改都會設置一個全局惟一的zxid值,若是zxid1小於zxid2,說明zxid1對應的操做比zxid2對應的事務先發生。
- Version numbers
每次對某節點進行更改,都會遞增這個節點的版本號。有三種版本號:
dataVersion:znode的更改次數。
cversion:子節點的更改次數。
aversion:節點的ACL的更改次數。
- Ticks
當使用多節點(這個節點表明的是組成ZooKeeper的server,而非znode)的ZooKeeper集羣時,各節點使用ticks來定義事件的時間。例如傳播狀態、會話超時時間、節點間鏈接超時時間等。tick時間間接設置了會話鏈接的最小超時時長(tick的兩倍時長)。若是客戶端在2倍tick時間內尚未成功鏈接server,那麼鏈接失敗。
- Real time
除了在建立和修改znode時會將當前實時時間戳放入stat結構以外,ZooKeeper根本不使用實時時間或時鐘時間。
9)znode的狀態
czxid 建立znode的zxid
mzxid 最近一次修改znode的zxid(建立、刪除、set直系子節點、set自身節點都會計數)
pzxid 最近一次修改子節點的zxid(建立、刪除直系子節點都會計數,set子節點不會計數)
ctime 建立znode的時間,單位毫秒
mtime 最近一次修改znode的時間,單位毫秒
version 修改znode的次數
cversion 修改子節點的次數(建立、刪除直系子節點都會計數,set子節點不會計數)
aversion 該znode的ACL修改次數
ephemeralOwner 臨時znode節點的session id,若是不是臨時節點,值爲0
dataLength znode 攜帶的數據長度,單位字節
numChildren 直系子節點的數量(不會遞歸計算孫節點)