ZooKeeper是一個開放源碼的分布式應用程序協調服務,它包含一個簡單的原語集,分布式應用程序可以基於它實現同步服務,配置維護和命名服務等。
Zookeeper設計目的
- 最終一致性:client不論連接到那個Server,展示給它的都是同一個視圖。
- 可靠性:具有簡單、健壯、良好的性能、如果消息m被到一台服務器接收,那么消息m將被所有服務器接收。
- 實時性:Zookeeper保證客戶端將在一個時間間隔范圍內獲得服務器的更新信息,或者服務器失效的信息。但由於網絡延時等原因,Zookeeper不能保證兩個客戶端能同時得到剛更新的數據,如果需要最新數據,應該在讀數據之前調用sync()接口。
- 等待無關(wait-free):慢的或者失效的client不得干預快速的client的請求,使得每個client都能有效的等待。
- 原子性:更新只能成功或者失敗,沒有中間狀態。
- 順序性:包括全局有序和偏序兩種:全局有序是指如果在一台服務器上消息a在消息b前發布,則在所有Server上消息a都將在消息b前被發布;偏序是指如果一個消息b在消息a后被同一個發送者發布,a必將排在b前面。
Zookeeper工作原理
在zookeeper的集群中,各個節點共有下面3種角色和4種狀態:
角色:leader、follower、observer
狀態:leading、following、observing、looking
Zookeeper的核心是原子廣播,這個機制保證了各個Server之間的同步。實現這個機制的協議叫做Zab協議(ZooKeeper Atomic Broadcast protocol)。Zab協議有兩種模式,它們分別是恢復模式(Recovery選主)和廣播模式(Broadcast同步)。當服務啟動或者在領導者崩潰后,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了和leader的狀態同步以后,恢復模式就結束了。狀態同步保證了leader和Server具有相同的系統狀態。
為了保證事務的順序一致性,zookeeper采用了遞增的事務id號(zxid)來標識事務。所有的提議(proposal)都在被提出的時候加上了zxid。實現中zxid是一個64位的數字,它高32位是epoch用來標識leader關系是否改變,每次一個leader被選出來,它都會有一個新的epoch,標識當前屬於那個leader的統治時期。低32位用於遞增計數。
每個Server在工作過程中有4種狀態:
- LOOKING:當前Server不知道leader是誰,正在搜尋。
- LEADING:當前Server即為選舉出來的leader。
- FOLLOWING:leader已經選舉出來,當前Server與之同步。
- OBSERVING:observer的行為在大多數情況下與follower完全一致,但是他們不參加選舉和投票,而僅僅接受(observing)選舉和投票的結果。
Zookeeper數據與存儲
1. 內存數據
Zookeeper的數據模型是樹結構,在內存數據庫中,存儲了整棵樹的內容,包括所有的節點路徑、節點數據、ACL信息,Zookeeper會定時將這個數據存儲到磁盤上。
1) DataTree
DataTree是內存數據存儲的核心,是一個樹結構,代表了內存中一份完整的數據。DataTree不包含任何與網絡、客戶端連接及請求處理相關的業務邏輯,是一個獨立的組件。
2) DataNode
DataNode是數據存儲的最小單元,其內部除了保存了結點的數據內容、ACL列表、節點狀態之外,還記錄了父節點的引用和子節點列表兩個屬性,其也提供了對子節點列表進行操作的接口。
3) KDatabase
Zookeeper的內存數據庫,管理Zookeeper的所有會話、DataTree存儲和事務日志。ZKDatabase會定時向磁盤dump快照數據,同時在Zookeeper啟動時,會通過磁盤的事務日志和快照文件恢復成一個完整的內存數據庫。
2. 事務日志
1) 文件存儲
在配置Zookeeper集群時需要配置dataDir目錄,其用來存儲事務日志文件。也可以為事務日志單獨分配一個文件存儲目錄:dataLogDir。若配置dataLogDir為/data/zookeeper-3.5.3-beta/logs,那么Zookeeper在運行過程中會在該目錄下建立一個名字為version-2的子目錄,該目錄確定了當前Zookeeper使用的事務日志格式版本號,當下次某個Zookeeper版本對事務日志格式進行變更時,此目錄也會變更,即在version-2子目錄下會生成一系列文件大小一致(64MB)的文件。
2) 日志格式
在配置好日志文件目錄,啟動Zookeeper后, 會在zoo.cfg文件里配置的日志目錄/data/zookeeper-3.5.3-beta/logs/version-2下產生日志文件, 如:
3) 日志寫入
FileTxnLog負責維護事務日志對外的接口,包括事務日志的寫入和讀取等。Zookeeper的事務日志寫入過程大體可以分為如下6個步驟:
a) 確定是否有事務日志可寫。當Zookeeper服務器啟動完成需要進行第一次事務日志的寫入,或是上一次事務日志寫滿時,都會處於與事務日志文件斷開的狀態,即Zookeeper服務器沒有和任意一個日志文件相關聯。因此在進行事務日志寫入前,Zookeeper首先會判斷FileTxnLog組件是否已經關聯上一個可寫的事務日志文件。若沒有,則會使用該事務操作關聯的ZXID作為后綴創建一個事務日志文件,同時構建事務日志的文件頭信息,並立即寫入這個事務日志文件中去,同時將該文件的文件流放入streamToFlush集合,該集合用來記錄當前需要強制進行數據落盤的文件流。
b) 確定事務日志文件是否需要擴容(預分配)。Zookeeper會采用磁盤空間預分配策略。當檢測到當前事務日志文件剩余空間不足4096字節時,就會開始進行文件空間擴容,即在現有文件大小上,將文件增加65536KB(64MB),然后使用"0"填充被擴容的文件空間。
c) 事務序列化。對事務頭和事務體的序列化,其中事務體又可分為會話創建事務、節點創建事務、節點刪除事務、節點數據更新事務等。
d) 生成Checksum。為保證日志文件的完整性和數據的准確性,Zookeeper在將事務日志寫入文件前,會計算生成Checksum。
e) 寫入事務日志文件流。將序列化后的事務頭、事務體和Checksum寫入文件流中,此時並為寫入到磁盤上。
f) 事務日志刷入磁盤。由於步驟5中的緩存原因,無法實時地寫入磁盤文件中,因此需要將緩存數據強制刷入磁盤。
4) 日志截斷
在Zookeeper運行過程中,可能出現非Leader記錄的事務ID比Leader上大,這是非法運行狀態。此時,需要保證所有機器必須與該Leader的數據保持同步,即Leader會發送TRUNC命令給該機器,要求進行日志截斷,Learner收到該命令后,就會刪除所有包含或大於該事務ID的事務日志文件。
3. snapshot-數據快照
數據快照是Zookeeper數據存儲中非常核心的運行機制,數據快照用來記錄Zookeeper服務器上某一時刻的全量內存數據內容,並將其寫入指定的磁盤文件中。
1) 文件存儲
與事務文件類似,Zookeeper快照文件也可以指定特定磁盤目錄,通過dataDir屬性來配置。若指定dataDir為/home/admin/zkData/zk_data,則在運行過程中會在該目錄下創建version-2的目錄,該目錄確定了當前Zookeeper使用的快照數據格式版本號。在Zookeeper運行時,會生成一系列文件。例如:
2) 數據快照
FileSnap負責維護快照數據對外的接口,包括快照數據的寫入和讀取等,將內存數據庫寫入快照數據文件其實是一個序列化過程。針對客戶端的每一次事務操作,Zookeeper都會將他們記錄到事務日志中,同時也會將數據變更應用到內存數據庫中,Zookeeper在進行若干次事務日志記錄后,將內存數據庫的全量數據Dump到本地文件中,這就是數據快照。其步驟如下:
a) 確定是否需要進行數據快照。每進行一次事務日志記錄之后,Zookeeper都會檢測當前是否需要進行數據快照,考慮到數據快照對於Zookeeper機器的影響,需要盡量避免Zookeeper集群中的所有機器在同一時刻進行數據快照。采用過半隨機策略進行數據快照操作。
b) 切換事務日志文件。表示當前的事務日志已經寫滿,需要重新創建一個新的事務日志。
c) 創建數據快照異步線程。創建單獨的異步線程來進行數據快照以避免影響Zookeeper主流程。
d) 獲取全量數據和會話信息。從ZKDatabase中獲取到DataTree和會話信息。
e) 生成快照數據文件名。Zookeeper根據當前已經提交的最大ZXID來生成數據快照文件名。
f) 數據序列化。首先序列化文件頭信息,然后再對會話信息和DataTree分別進行序列化,同時生成一個Checksum,一並寫入快照數據文件中去。
4. 初始化
在Zookeeper服務器啟動期間,首先會進行數據初始化工作,用於將存儲在磁盤上的數據文件加載到Zookeeper服務器內存中。
1) 初始化流程
Zookeeper的初始化過程如下圖所示:
數據的初始化工作是從磁盤上加載數據的過程,主要包括了從快照文件中加載快照數據和根據實物日志進行數據修正兩個過程。
初始化FileTxnSnapLog: FileTxnSnapLog是Zookeeper事務日志和快照數據訪問層,用於銜接上層業務和底層數據存儲,底層數據包含了事務日志和快照數據兩部分。FileTxnSnapLog中對應FileTxnLog和FileSnap。
初始化ZKDatabase: 首先構建DataTree,同時將FileTxnSnapLog交付ZKDatabase,以便內存數據庫能夠對事務日志和快照數據進行訪問。在ZKDatabase初始化時,DataTree也會進行相應的初始化工作,如創建一些默認結點,如/、/zookeeper、/zookeeper/quota三個節點。
創建PlayBackListener: 其主要用來接收事務應用過程中的回調,在Zookeeper數據恢復后期,會有事務修正過程,此過程會回調PlayBackListener來進行對應的數據修正。
處理快照文件: 此時可以從磁盤中恢復數據了,首先從快照文件開始加載。
獲取最新的100個快照文件: 更新時間最晚的快照文件包含了最新的全量數據。
解析快照文件: 逐個解析快照文件,此時需要進行反序列化,生成DataTree和sessionsWithTimeouts,同時還會校驗Checksum及快照文件的正確性。對於100個快找文件,如果正確性校驗通過時,通常只會解析最新的那個快照文件。只有最新快照文件不可用時,才會逐個進行解析,直至100個快照文件全部解析完。若將100個快照文件解析完后還是無法成功恢復一個完整的DataTree和sessionWithTimeouts,此時服務器啟動失敗。
獲取最新的ZXID: 此時根據快照文件的文件名即可解析出最新的ZXID:zxid_for_snap。該ZXID代表了Zookeeper開始進行數據快照的時刻。
處理事務日志: 此時服務器內存中已經有了一份近似全量的數據,現在開始通過事務日志來更新增量數據。
獲取所有zxid_for_snap之后提交的事務: 此時,已經可以獲取快照數據的最新ZXID。只需要從事務日志中獲取所有ZXID比步驟7得到的ZXID大的事務操作。
事務應用: 獲取大於zxid_for_snap的事務后,將其逐個應用到之前基於快照數據文件恢復出來的DataTree和sessionsWithTimeouts。每當有一個事務被應用到內存數據庫中后,Zookeeper同時會回調PlayBackListener,將這事務操作記錄轉換成Proposal,並保存到ZKDatabase的committedLog中,以便Follower進行快速同步。
獲取最新的ZXID: 待所有的事務都被完整地應用到內存數據庫中后,也就基本上完成了數據的初始化過程,此時再次獲取ZXID,用來標識上次服務器正常運行時提交的最大事務ID。
校驗epoch: epoch標識了當前Leader周期,集群機器相互通信時,會帶上這個epoch以確保彼此在同一個Leader周期中。完成數據加載后,Zookeeper會從步驟11中確定ZXID中解析出事務處理的Leader周期:epochOfZxid。同時也會從磁盤的currentEpoch和acceptedEpoch文件中讀取上次記錄的最新的epoch值,進行校驗。
5. 數據同步
整個集群完成Leader選舉后,Learner會向Leader進行注冊,當Learner向Leader完成注冊后,就進入數據同步環節,同步過程就是Leader將那些沒有在Learner服務器上提交過的事務請求同步給Learner服務器,大體過程如下:
獲取Learner狀態: 在注冊Learner的最后階段,Learner服務器會發送給Leader服務器一個ACKEPOCH數據包,Leader會從這個數據包中解析出該Learner的currentEpoch和lastZxid。
數據同步初始化: 首先從Zookeeper內存數據庫中提取出事務請求對應的提議緩存隊列proposals,同時完成peerLastZxid(該Learner最后處理的ZXID)、minCommittedLog(Leader提議緩存隊列commitedLog中最小的ZXID)、maxCommittedLog(Leader提議緩存隊列commitedLog中的最大ZXID)三個ZXID值的初始化。
對於集群數據同步而言,通常分為四類: 直接差異化同步(DIFF同步)、先回滾再差異化同步(TRUNC+DIFF同步)、僅回滾同步(TRUNC同步)、全量同步(SNAP同步),在初始化階段,Leader會優先以全量同步方式來同步數據。同時,會根據Leader和Learner之間的數據差異情況來決定最終的數據同步方式。
-> 直接差異化同步(DIFF同步: peerLastZxid介於minCommittedLog和maxCommittedLog之間)。Leader首先向這個Learner發送一個DIFF指令,用於通知Learner進入差異化數據同步階段,Leader即將把一些Proposal同步給自己,針對每個Proposal,Leader都會通過發送PROPOSAL內容數據包和COMMIT指令數據包來完成,
-> 先回滾再差異化同步(TRUNC+DIFF同步: Leader已經將事務記錄到本地事務日志中,但是沒有成功發起Proposal流程)。當Leader發現某個Learner包含了一條自己沒有的事務記錄,那么就需要該Learner進行事務回滾,回滾到Leader服務器上存在的,同時也是最接近於peerLastZxid的ZXID。
-> 僅回滾同步(TRUNC同步: peerLastZxid大於maxCommittedLog)。Leader要求Learner回滾到ZXID值為maxCommittedLog對應的事務操作。
-> 全量同步(SNAP同步: peerLastZxid小於minCommittedLog或peerLastZxid不等於lastProcessedZxid)。Leader無法直接使用提議緩存隊列和Learner進行同步,因此只能進行全量同步。Leader將本機的全量內存數據同步給Learner。Leader首先向Learner發送一個SNAP指令,通知Learner即將進行全量同步,隨后,Leader會從內存數據庫中獲取到全量的數據節點和會話超時時間記錄器,將他們序列化后傳輸給Learner。Learner接收到該全量數據后,會對其反序列化后載入到內存數據庫中。
Zookeeper集群節點
- Zookeeper節點部署越多,服務的可靠性越高,建議部署奇數個節點,因為zookeeper集群是以宕機個數過半才會讓整個集群宕機的。
- 需要給每個zookeeper 1G左右的內存,如果可能的話,最好有獨立的磁盤,因為獨立磁盤可以確保zookeeper是高性能的。如果你的集群負載很重,不要把zookeeper和RegionServer運行在同一台機器上面,就像DataNodes和TaskTrackers一樣。
ZooKeeper常用命令
Zookeeper服務端命令
啟動ZK服務: sh bin/zkServer.sh start
查看ZK服務狀態: sh bin/zkServer.sh status
停止ZK服務: sh bin/zkServer.sh stop
重啟ZK服務: sh bin/zkServer.sh restart
Zookeeper客戶端命令
客戶端登錄Zookeeper: sh bin/zkCli.sh -server 127.0.0.1:2181
顯示根目錄下、文件: ls / #使用 ls 命令來查看當前ZooKeeper中所包含的內容。即查看Zookeeper當前注冊了哪些服務?
顯示根目錄下、文件: ls2 / #查看當前節點數據並能看到更新次數等數據
創建文件,並設置初始內容: create /zk "test" #創建一個新的znode節點"zk"以及與它關聯的字符串
獲取文件內容: get /zk #確認znode是否包含我們所創建的字符串
修改文件內容: set /zk "zkbak" #對zk所關聯的字符串進行設置
刪除文件: delete /zk #將剛才創建的znode刪除
退出客戶端: quit
幫助命令: help
ZooKeeper的"watch"機制說明
zookeeper作為一款成熟的分布式協調框架,訂閱-發布功能是很重要的一個。所謂訂閱發布功能,其實說白了就是觀察者模式。觀察者會訂閱一些感興趣的主題,然后這些主題一旦變化了,就會自動通知到這些觀察者。
1) zookeeper的watch機制原理
zookeeper的訂閱發布也就是watch機制,是一個輕量級的設計,因為它采用了一種推拉結合的模式。一旦服務端感知主題變了,那么只會發送一個事件類型和節點信息給關注的客戶端,而不會包括具體的變更內容,所以事件本身是輕量級的,這就是所謂的"推"部分,然后收到變更通知的客戶端需要自己去拉變更的數據,這就是"拉"部分。znode以某種方式發生變化時,"觀察"(watch)機制可以讓客戶端得到通知.可以針對ZooKeeper服務的“操作”來設置觀察,該服務的其他 操作可以觸發觀察。
訂閱-發布在zookeeper中是通過事件注冊和回調機制實現的,整個注冊回調過程分為三個大的部分:客戶端注冊,服務端發回事件,客戶端回調。
2) zookeeper的watch架構
watch的整體流程如下圖所示,客戶端先向zookeeper服務端成功注冊想要監聽的節點狀態,同時客戶端本地會存儲該監聽器相關的信息在WatchManager中,當zookeeper服務端監聽的數據狀態發生變化時,zookeeper就會主動通知發送相應事件信息給相關會話客戶端,客戶端就會在本地響應式的回調相關watcher的Handler。
3) zookeeper的watch特性
watch是一次性的,每次都需要重新注冊,並且客戶端在會話異常結束時不會收到任何通知,而快速重連接時仍不影響接收通知。watch的回調執行都是順序執行的,並且客戶端在沒有收到關注數據的變化事件通知之前是不會看到最新的數據,另外需要注意不要在Watch回調邏輯中阻塞整個客戶端的watch回調。watch是輕量級的,WatchEvent是最小的通信單元,結構上只包含通知狀態、事件類型和節點路徑。ZooKeeper服務端只會通知客戶端發生了什么,並不會告訴具體內容。
Zookeeper集群部署記錄
1)服務器信息 主機名 系統 IP地址 sign-zk01.wangshibo.cn CentOS release 6.8 172.16.51.198 sign-zk02.wangshibo.cn CentOS release 6.8 172.16.51.199 sign-zk03.wangshibo.cn CentOS release 6.8 172.16.51.200 2)安裝過程 先記錄sign-zk01.wangshibo.cn節點機的部署過程(其他兩個節點的部署過程基本一致) 安裝jdk 可以yum安裝自帶的jdk [root@sign-zk01 ~]# yum -y install java-1.7.0-openjdk* [root@sign-zk01 ~]# java -version java version "1.7.0_151" OpenJDK Runtime Environment (rhel-2.6.11.0.el6_9-x86_64 u151-b00) OpenJDK 64-Bit Server VM (build 24.151-b00, mixed mode) 不過建議卸載掉自帶的jdk,安裝Java OpenJDK [root@sign-zk01 ~]# yum -y remove java-1.7.0-openjdk* [root@sign-zk01 ~]# yum -y remove tzdata-java.noarch [root@sign-zk01 ~]# java -version -bash: java: command not found [root@sign-zk01 ~]# rpm -ivh jdk-7u79-linux-x64.rpm --force [root@sign-zk01 ~]# java -version java version "1.7.0_79" Java(TM) SE Runtime Environment (build 1.7.0_79-b15) Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode) 安裝並部署zookeeper集群 [app@sign-zk01 ~]$ cd /usr/local/src/ [app@sign-zk01 src]$ wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.11/zookeeper-3.4.11.tar.gz [app@sign-zk01 src]$ tar -zvxf zookeeper-3.4.11.tar.gz [app@sign-zk01 src]$ mv zookeeper-3.4.11.tar.gz /data/ [app@sign-zk01 src]$ cd /data/zookeeper-3.4.11/conf/ [app@sign-zk01 conf]$ cp zoo_sample.cfg zoo.cfg [app@sign-zk01 conf]$ cat zoo.cfg |grep -v "#" tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper-3.4.11/data dataLogDir=/data/zookeeper-3.4.11/logs clientPort=2181 server.1=sign-zk01.wangshibo.cn:2888:3888 server.2=sign-zk02.wangshibo.cn:2888:3888 server.3=sign-zk03.wangshibo.cn:2888:3888 參數說明: tickTime這個時間是作為zookeeper服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是說每個tickTime時間就會發送一個心跳。 initLimit這個配置項是用來配置zookeeper接受客戶端(這里所說的客戶端不是用戶連接zookeeper服務器的客戶端,而是zookeeper服務器集群中連接到leader的follower 服務器)初始化連接時最長能忍受多少個心跳時間間隔數。 當已經超過10個心跳的時間(也就是tickTime)長度后 zookeeper 服務器還沒有收到客戶端的返回信息,那么表明這個客戶端連接失敗。總的時間長度就是 10*2000=20秒。 syncLimit這個配置項標識leader與follower之間發送消息,請求和應答時間長度,最長不能超過多少個tickTime的時間長度,總的時間長度就是5*2000=10秒。 dataDir顧名思義就是zookeeper保存數據的目錄,默認情況下zookeeper將寫數據的日志文件也保存在這個目錄里; clientPort這個端口就是客戶端連接Zookeeper服務器的端口,Zookeeper會監聽這個端口接受客戶端的訪問請求; server.A=B:C:D中的A是一個數字,表示這個是第幾號服務器,B是這個服務器的IP地址,C第一個端口用來集群成員的信息交換,表示這個服務器與集群中的leader服務器交換信息的端口,D是在leader掛掉時專門用來進行選舉leader所用的端口。 [app@sign-zk01 conf]$ mkdir /data/zookeeper-3.4.11/data [app@sign-zk01 conf]$ mkdir /data/zookeeper-3.4.11/logs 創建ServerID標識(這是三個節點機器唯一配置不一樣的地方,三個節點的myid文件配置的ServerID不能重復,建議分別配置成1、2、3) 除了修改zoo.cfg配置文件外,zookeeper集群模式下還要配置一個myid文件,這個文件需要放在dataDir目錄下。 這個文件里面有一個數據就是A的值(該A就是zoo.cfg文件中server.A=B:C:D中的A),在zoo.cfg文件中配置的dataDir路徑中創建myid文件。 [app@sign-zk01 ~]$ echo "1" > /data/zookeeper-3.4.11/data/myid [app@sign-zk02 ~]$ echo "2" > /data/zookeeper-3.4.11/data/myid [app@sign-zk03 ~]$ echo "3" > /data/zookeeper-3.4.11/data/myid 然后分別重啟三個節點機器的zookeeper(stop/start/restart) [app@sign-zk01 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh start [app@sign-zk02 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh start [app@sign-zk03 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh start [app@sign-zk01 ~]$ lsof -i:2181 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 7071 app 20u IPv6 30989 0t0 TCP *:eforward (LISTEN) 查看zookeeper集群狀態 [app@sign-zk01 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /data/zookeeper-3.4.11/bin/../conf/zoo.cfg Mode: follower [app@sign-zk02 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /data/zookeeper-3.4.11/bin/../conf/zoo.cfg Mode: leader [app@sign-zk03 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /data/zookeeper-3.4.11/bin/../conf/zoo.cfg Mode: follower 從上面可以看出,sign-zk01.wangshibo.cn,sign-zk03.wangshibo.cn兩台服務器zookeeper的狀態是follow模式, sign-zk02.wangshibo.cn這台服務器zookeeper的狀態是leader模式。 Zookeeper集群連接 Zookeeper集群搭建完畢之后,可以通過客戶端腳本連接到zookeeper集群上面,對客戶端來說,zookeeper集群是一個整體,連接到zookeeper集群實際上感覺在獨享整個集群的服務。 [app@sign-zk01 ~]$ /data/zookeeper-3.4.11/bin/zkCli.sh -server sign-zk02.wangshibo.cn:2181 Connecting to sign-zk02.wangshibo.cn:2181 2017-11-22 15:22:55,110 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.11-37e277162d567b55a07d1755f0b31c32e93c01a0, built on 11/01/2017 18:06 GMT 2017-11-22 15:22:55,113 [myid:] - INFO [main:Environment@100] - Client environment:host.name=sign-zk01.wangshibo.cn 2017-11-22 15:22:55,113 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.7.0_79 2017-11-22 15:22:55,114 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=Oracle Corporation 2017-11-22 15:22:55,114 [myid:] - INFO [main:Environment@100] - Client environment:java.home=/usr/java/jdk1.7.0_79/jre 2017-11-22 15:22:55,114 [myid:] - INFO [main:Environment@100] - Client environment:java.class.path=/data/zookeeper-3.4.11/bin/../build/classes:/data/zookeeper-3.4.11/bin/../build/lib/*.jar:/data/zookeeper-3.4.11/bin/../lib/slf4j-log4j12-1.6.1.jar:/data/zookeeper-3.4.11/bin/../lib/slf4j-api-1.6.1.jar:/data/zookeeper-3.4.11/bin/../lib/netty-3.10.5.Final.jar:/data/zookeeper-3.4.11/bin/../lib/log4j-1.2.16.jar:/data/zookeeper-3.4.11/bin/../lib/jline-0.9.94.jar:/data/zookeeper-3.4.11/bin/../lib/audience-annotations-0.5.0.jar:/data/zookeeper-3.4.11/bin/../zookeeper-3.4.11.jar:/data/zookeeper-3.4.11/bin/../src/java/lib/*.jar:/data/zookeeper-3.4.11/bin/../conf: 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=<NA> 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=amd64 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:os.version=2.6.32-696.13.2.el6.x86_64 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:user.name=app 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/home/app 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:user.dir=/home/app 2017-11-22 15:22:55,116 [myid:] - INFO [main:ZooKeeper@441] - Initiating client connection, connectString=sign-zk02.wangshibo.cn:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@594b7042 Welcome to ZooKeeper! 2017-11-22 15:22:55,136 [myid:] - INFO [main-SendThread(sign-zk02.wangshibo.cn:2181):ClientCnxn$SendThread@1035] - Opening socket connection to server sign-zk02.wangshibo.cn/172.16.51.199:2181. Will not attempt to authenticate using SASL (unknown error) 2017-11-22 15:22:55,140 [myid:] - INFO [main-SendThread(sign-zk02.wangshibo.cn:2181):ClientCnxn$SendThread@877] - Socket connection established to sign-zk02.wangshibo.cn/172.16.51.199:2181, initiating session JLine support is enabled [zk: sign-zk02.wangshibo.cn:2181(CONNECTING) 0] 2017-11-22 15:22:55,287 [myid:] - INFO [main-SendThread(sign-zk02.wangshibo.cn:2181):ClientCnxn$SendThread@1302] - Session establishment complete on server sign-zk02.wangshibo.cn/172.16.51.199:2181, sessionid = 0x2000495ce4f0000, negotiated timeout = 30000 WATCHER:: WatchedEvent state:SyncConnected type:None path:null //這一步按Enter [zk: sign-zk02.wangshibo.cn:2181(CONNECTED) 0] ls / [zookeeper] [zk: sign-zk02.wangshibo.cn:2181(CONNECTED) 1] quit Quitting... 2017-11-22 15:23:39,255 [myid:] - INFO [main:ZooKeeper@687] - Session: 0x2000495ce4f0000 closed 2017-11-22 15:23:39,257 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x2000495ce4f0000 通過上圖可以看出整個zookeeper集群已經搭建並測試完成。
問題記錄
客戶端連接zookeeper,發生重連,拒絕連接,如下截圖(172.16.50.86/87/88是zk集群的三個節點):
官網解釋:
解決辦法:
修改三個節點的zookeeper里conf的zoo.cfg.dynamic.*文件 [app@uatsign-zk01 ~]$ cat /data/zookeeper-3.5.3-beta/conf/zoo.cfg.dynamic.800000000 server.1=172.16.50.86:2888:3888:participant; server.2=172.16.50.87:2888:3888:participant; server.3=172.16.50.88:2888:3888:participant; 修改為 [app@uatsign-zk01 ~]$ cat /data/zookeeper-3.5.3-beta/conf/zoo.cfg.dynamic.800000000 server.1=172.16.50.86:2888:3888:participant;172.16.50.86:2181 server.2=172.16.50.87:2888:3888:participant;172.16.50.87:2181 server.3=172.16.50.88:2888:3888:participant;172.16.50.88:2181 然后重啟每個節點的zookeeper服務即可 注意: 上面修改中的172.16.50.86/87/88:2181不能配置成0.0.0.0:2181,得加具體的客戶端ip(即具體的每個zk節點的ip),如果加全0,重連的時候就會用全0的ip,可能會出錯。
Zookeeper日志清理機制 (清理zookeeper的歷史數據)
問題記錄:
在服務器上部署了zookeeper集群(三個節點), 在一次斷電重啟后發現, zookeeper服務在啟動后不就就自動關閉.
后面發現是集群中的三個zk節點的數據盤使用率100%導致的! 發現是zoo.cfg文件里配置的dataDir目錄 (即/data/zookeeper-3.5.3-beta/data) 占用了數據盤很大空間!
解決辦法: 清理Zookeeper的歷史數據
1) 使用zookeeper自帶的清理腳本zkCleanup.sh, 手動清理zookeeper歷史數據
[app@uatsign-zk03 ~]$ /data/zookeeper-3.5.3-beta/bin/zkCleanup.sh -n 5 ....................... Removing file: Jul 17, 2018 3:07:46 PM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.b0022de4e 2019-04-01 15:45:22,943 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Oct 7, 2018 7:20:30 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.c0022e577 Removing file: Oct 7, 2018 7:20:30 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.c0022e577 2019-04-01 15:45:22,944 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Mar 13, 2019 11:55:47 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.23000cc8ff Removing file: Mar 13, 2019 11:55:47 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.23000cc8ff 2019-04-01 15:45:22,948 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Mar 12, 2019 6:50:44 PM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.2300056f0c Removing file: Mar 12, 2019 6:50:44 PM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.2300056f0c 2019-04-01 15:45:22,951 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Nov 24, 2017 9:41:31 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.0 ........................... [app@uatsign-zk03 ~]$ ll /data/zookeeper-3.5.3-beta/data/version-2/ total 2914360 -rw-rw-r-- 1 app app 2 Apr 1 15:46 acceptedEpoch -rw-rw-r-- 1 app app 2 Apr 1 15:46 currentEpoch -rw-rw-r-- 1 app app 334514719 Apr 1 11:45 snapshot.2c00013653 -rw-rw-r-- 1 app app 94838784 Apr 1 11:55 snapshot.2c00022aea -rw-rw-r-- 1 app app 186249216 Apr 1 14:36 snapshot.2d0001116d -rw-r--r-- 1 root root 338225939 Apr 1 15:31 snapshot.2e00001294 -rw-r--r-- 1 root root 338224028 Apr 1 15:41 snapshot.2e000148d5
其中: -n 5 表示清理后保留5份最新的歷史數據
手動清理zookeeper歷史數據的命令總結
# /data/zookeeper-3.5.3-beta/bin/zkCleanup.sh -n 5 之前版本, 需要在清理命令中添加zookeeper的數據目錄 # /data/zookeeper-3.5.3-beta/bin/zkCleanup.sh /data/zookeeper-3.5.3-beta/data -n 5
2) 設置zookeeper歷史數據的自動清理
修改zoo.cfg配置文件中的 autopurge.snapRetainCount 和 autopurge.purgeInterval 兩個參數實現定時清理
依次修改zookeeper集群節點的zoo.cfg文件, 添加下面兩行內容: [app@uatsign-zk03 ~]$ cat /data/zookeeper-3.5.3-beta/conf/zoo.cfg ............. autopurge.snapRetainCount=10 autopurge.purgeInterval=1 然后依次重啟集群節點的zookeeper服務即可 [app@uatsign-zk03 ~]$ ps -ef|grep zookeeper|awk '{print $2}'|xargs kill -9 [app@uatsign-zk03 ~]$ /data/zookeeper-3.5.3-beta/bin/zkServer.sh start
autopurge.purgeInterval: 這個參數指定了清理頻率,單位是小時,需要填寫一個1或更大的整數,默認是0,表示不開啟自動清理功能。
autopurge.snapRetainCount: 這個參數和上面的參數搭配使用,這個參數指定了需要保留的快照文件數目,默認是保留3個。