[TOC]
一、Zookeeper簡介
ZooKeeper致力于提供一個高性能、高可用,且具備嚴格的順序訪問控制能力的分布式協(xié)調(diào)服務,是雅虎公司創(chuàng)建,是Google的Chubby一個開源的實現(xiàn),也是Hadoop和Hbase的重要組件.

設計目標:
- 簡單的數(shù)據(jù)結(jié)構(gòu):共享的樹形結(jié)構(gòu),類似文件系統(tǒng),存儲于內(nèi)存
- 可以構(gòu)建集群:避免單點故障,3-5臺機器就可以組成集群,超過半數(shù)正常工作就能對外提供服務;
- 順序訪問:對于每個讀請求,zk會分配一個全局唯一的遞增編號,利用這個特性可以實現(xiàn)高級協(xié)調(diào)服務
- 高性能:基于內(nèi)存操作,服務于非事務請求,適用于讀操作為主的業(yè)務場景。3臺zk集群能達到13w QPS
二、分布式系統(tǒng)協(xié)調(diào)“方法論”
1.CAP理論
- C 一致性:數(shù)據(jù)在分布式環(huán)境下的多個副本之間能否保持一致性,這里的一致性更多是指強一致性;
- A 可用性:分布式系統(tǒng)一直處于可用狀態(tài),對于請求總是能在有限的時間 內(nèi)返回結(jié)果致性;
- P 分區(qū)容錯性:除非整個網(wǎng)絡故障,分布式系統(tǒng)在任何網(wǎng)絡或者單點故障時,仍能對外提供滿足一致性和可用性的服務;
CAP理論:一個分布式系統(tǒng)不可能同時滿足一致性、可用性和分區(qū)容錯性這三個 基本需求,最多只能同時滿足其中的兩項;

TIPS:架構(gòu)師的精力往往就花在怎么樣根據(jù)業(yè)務場景在A和C直接尋求平衡;
2.BASE理論
- Basically Avaliable 基本可用:當分布式系統(tǒng)出現(xiàn)不可預見的故障時,允許損失部分可用性,保障系統(tǒng)的“基本可用”;體現(xiàn)在“時間上的損失”和“功能上的損失”;e.g:部分用戶雙十一高峰期淘寶頁面卡頓或 降級處理;
- Soft state 軟狀態(tài):允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài),既系統(tǒng)的不同節(jié)點的數(shù)據(jù)副本之間的數(shù)據(jù)同步過程存在延時,并認為這種延時不會影響系統(tǒng)可用性;e.g:12306網(wǎng)站賣火車票,請求會進入排隊隊列;
- Eventually consistent 最終一致性:所有的數(shù)據(jù)在經(jīng)過一段時間的數(shù)據(jù)同步后,最終能夠達到一個一致的狀態(tài);e.g:理財產(chǎn)品首頁充值總金額 短時不一致;
BASE理論:即使無法做到強一致性,但分布式系統(tǒng)可以根據(jù)自己的業(yè)務特點,采 用適當?shù)姆绞絹硎瓜到y(tǒng)達到最終的一致性;
三、分布式環(huán)境協(xié)調(diào)通信應用場景
Zookeeper常應用以下場景:
- 數(shù)據(jù)發(fā)布訂閱
- 負載均衡
- 命名服務
- Master選舉
- 集群管理
- 配置管理
- 分布式隊列
- 分布式鎖
四、Zookeeper配置文件詳解
| 序號 | 參數(shù)名 | 說明 |
|---|---|---|
| 1 | clientPort | 客戶端連接server的端口,即對外服務端口,一般設置為2181 |
| 2 | dataDir | 存儲快照文件snapshot的目錄。默認情況下,事務日志也會存儲在這里。建議同時配置參數(shù)dataLogDir, 事務日志的寫性能直接影響zk性能。 |
| 3 | tickTime | ZK中的一個時間單元。ZK中所有時間都是以這個時間單元為基礎,進行整數(shù)倍配置的。例如,session的最小超時時間是2*tickTime |
| 4 | dataLogDir | 事務日志輸出目錄。盡量給事務日志的輸出配置單獨的磁盤或是掛載點,這將極大的提升ZK性能 |
| 5 | globalOutstandingLimit | 最大請求堆積數(shù)。默認是1000。ZK運行的時候, 盡管server已經(jīng) 沒有空閑來處理更多的客戶端請求了,但是還是允許客戶端將請求提交到服務器上來,以提高吞吐性能。當然,為了防止Server內(nèi)存 溢出,這個請求堆積數(shù)還是需要限制下的。(Java system property: zookeeper.globalOutstandingLimit.) |
| 6 | preAllocSize | 預先開辟磁盤空間,用于后續(xù)寫入事務日志。默認是64M,每個事務日志大小就是64M。如果ZK的快照頻率較大的話,建議適當減小 這個參數(shù)。(Java system property: zookeeper.preAllocSize) |
| 7 | snapCount | 每進行snapCount次事務日志輸出后,觸發(fā)一次快照(snapshot),此時,ZK會生成一個snapshot.文件,同時創(chuàng)建一個新的事務日志文件log.。 默認是100000.(真正的代碼實現(xiàn)中,會進行一定的隨機數(shù)處理,以避 免 所 有 服 務 器 在 同 一 時 間 進 行 快 照 而 影 響 性 能 )(Java system property: zookeeper.snapCount) |
| 8 | traceFile | 用于記錄所有請求的log,一般調(diào)試過程中可以使用,但是生產(chǎn)環(huán)境不建 議使用,會嚴重影響性能。(Java system property:? requestTraceFile) |
| 9 | maxClientCnxns | 單個客戶端與單臺服務器之間的連接數(shù)的限制,是ip級別的,默認是60如果設置為0,那么表明不作任何限制。請注意這個限制的使用范圍,僅僅是單臺客戶端機器與單臺ZK服務器之間的連接數(shù)限制,不是針對指定客戶端IP,也不是ZK集群的連接數(shù)限制,也不是單臺ZK對所有客戶端的 連接數(shù)限制。 |
| 10 | clientPortAddress | 對于多網(wǎng)卡的機器,可以為每個IP指定不同的監(jiān)聽端口。默認情況是所 有IP都監(jiān)聽 clientPort指定的端口。 New in 3.3.0 |
| 11 | minSessionTimeou tmaxSessionTimeout | Session超時時間限制,如果客戶端設置的超時時間不在這個范圍,那么會被強制設置為最大或最小時間。默認的Session超時時間是在2* tickTime ~ 20 * tickTime 這 個 范 圍 New in 3.3.0 |
| 12 | fsync.warningthresholdms | 事務日志輸出時,如果調(diào)用fsync方法超過指定的超時時間,那么會在日志中輸出警告信息,默認是1000ms |
| 13 | autopurge.purgeInterval | 清理任務的時間間隔,單位為hours |
| 14 | autopurge.snapRetainCount | zkserver啟動時會開啟一個org.apache.zookeeper.server.DatadirCleanupManager的線程,用于清理"過期"的snapshot文件和其相應的txn log file,此參數(shù)用于設定需要被retain保留的文件個數(shù)(從QuorumPeerMain跟蹤代碼) |
| 15 | electionAlg | 選舉算法,默認為3,可以選擇(0,1,2,3), 0表示使用原生的UDP(LeaderElection), 1表示使用費授權的UDP 2表示使用授權的UDP(AuthFastLeaderElection) 3基于TCP的快速選舉(FastLeaderElection) |
| 16 | initLimit | Leader與learner建立連接中 socket通訊read所阻塞的時間(initLimit * tickTime) 如果是Leaner數(shù)量較多或者leader的數(shù)量很大, 可以增加此值 |
| 17 | SyncLimit | learner與leader建立連接中,socket通訊read阻塞的時間.其中包括數(shù)據(jù)同步/數(shù)據(jù)提交等 |
| 18 | peerType | zkserver 類型 observer 觀察者, participant參與者 ,默認為參與者 |
| 19 | leaderServes | 系統(tǒng)屬性 zookeeper.leaderServes leader是否接受client請求,默認為yes即leader可以接受client的連接,在zk cluster 環(huán)境中,當節(jié)點數(shù)為>3時,建議關閉 |
| 20 | cnxTimeout | 系統(tǒng)屬性:zookeeper.cnxTimeout leader選舉時socket連接打開的時長,只有在electionAlg=3有效 |
| 21 | skipACL | 系統(tǒng)屬性:zookeeper.skipACL 默認為no,是否跳過ACL檢查 |
| 22 | forceSync | 系統(tǒng)屬性:zookeeper.forceSync 默認yes 在update執(zhí)行之前,是否強制對操作立即持久寫入txn log文件.關閉此選項,會造成服務器失效后,尚未持久化的數(shù)據(jù)丟失 |
| 23 | jute.maxbuffer | 每個節(jié)點最大數(shù)據(jù)量,是默認是1M。這個限制必須在server和client 端都進行設置才會生效。 |
五、Zookeeper特性
1.會話(Session)
客戶端與服務端的一次會話連接,本質(zhì)是TCP長連接,通過會話可以進行心跳檢測和數(shù)據(jù)傳輸;

2.數(shù)據(jù)節(jié)點(znode)
ZooKeeper的視圖結(jié)構(gòu)和標準的Unix文件系統(tǒng)類似,其中每個節(jié)點稱為“數(shù)據(jù)節(jié)點”或ZNode,每個znode可以存儲數(shù)據(jù),還可以掛載子節(jié)點,因此可以稱之為“樹”
特性:
- 在Zookeeper中,znode是一個跟Unix文件系統(tǒng)路徑相似的節(jié)點,可以往這個節(jié)點存儲或獲取數(shù)據(jù)。
- 通過客戶端可對znode進行增刪改 查的操作,還可以注冊watcher監(jiān) 控znode的變化。
Zookeeper節(jié)點類型
- 持久節(jié)點(Persistent)
- 持久順序節(jié)點(Persistent_Sequential)
- 臨時節(jié)點(Ephemeral)
- 臨時順序節(jié)點(Ephemeral_Sequential)
對于持久接地啊和臨時節(jié)點,同一個Znode下,節(jié)點的名稱是唯一的 -實現(xiàn)分布式鎖的基礎

Zookeeper節(jié)點狀態(tài)屬性

3.版本
4.Watcher
事件監(jiān)聽器,客戶端可以在節(jié)點上注冊監(jiān)聽器,當特定的事件發(fā)生后,zk會通知到感興趣的客戶 端;eventType: NodeCreated、NodeDeleted、NodeDataChanged、NodeChildrenChange
5.ACL
Zk采用ACL(access control lists)策略來控制權限,5種權限:create、read,write,delete,admin。
ACL機制,表示為scheme:id:permissions,第一個字段表示采用哪一種機制,第二個id表示用戶,permissions表示相關權限(如只讀,讀寫,管理等)。zookeeper提供了如下幾種機制( scheme):
world: 它下面只有一個id, 叫anyone, world:anyone代表任何人,zookeeper中對所有人有權限的結(jié)點就是屬于world:anyone的
auth: 它不需要id, 只要是通過authentication的user都有權限(zookeeper支持通過kerberos來進行authencation, 也支持username/password形式的authentication)
digest: 它對應的id為username:BASE64(SHA1(password)),它需要先通過username:password形式的authentication
ip: 它對應的id為客戶機的IP地址,設置的時候可以設置一個ip段,比如ip:192.168.1. 0/16, 表示匹配前16個bit的IP段
super: 在這種scheme情況下,對應的id擁有超級權限,可以做任何事情(cdrwa)
6.集群角色
六、常用客戶端命令
1.服務端常用命令
在準備好相應的配置之后可以直接通過zkServer.sh 這個腳本進行服務的相關操作:
- 啟動ZK服務: sh bin/zkServer.sh start
- 查看ZK服務狀態(tài): sh bin/zkServer.sh status
- 停止Zk服務: sh bin/zkServer.sh stop
- 重啟ZK服務: sh bin/zkServer.sh restart
2.客戶端常用命令
使用zkCli.sh -server 127.0.0.1:2181 連接到Zookeeper服務
- 顯示根目錄下文件、文件
ls/
- 顯示根目錄下、文件
ls2 /
查看當前節(jié)點數(shù)據(jù)并能看到更新次數(shù)等數(shù)據(jù)
- 創(chuàng)建文件并設置初始內(nèi)容
create /zk "test"
創(chuàng)建一個新的znode節(jié)點“zk" 以及與它關聯(lián)的字符串
- 獲取文件
get /zk
確認znode是否包含創(chuàng)建的字符串
- 修改文件內(nèi)容
set /zk "zkbak"
對zk所關聯(lián)的字符串進行設置和修改
- 刪除文件
delete /zk
將創(chuàng)建的znode節(jié)點進行刪除,如果存在子節(jié)點則刪除會失敗
- 遞歸刪除
rmr /zk
將剛才創(chuàng)建的znode刪除,同時刪除子節(jié)點
- 退出客戶端
quit
- 幫助命令
help
七、ACL 常用命令
1. getAcl
獲取指定節(jié)點的ACL信息
2.setACL
設置指定節(jié)點的ACL信息
3.addauth
注冊會話授權信息
八、Zookeeper常用命令
- 查看哪個節(jié)點被選擇作為follower或者leader
echo stat|nc 127.0.0.1 2181
- 測試是否啟動了該Server,若回復imok表示已經(jīng)啟動
echo ruok|nc 127.0.0.1 2181
- 列出未經(jīng)處理的會話和臨時節(jié)點
echo kill | nc 127.0.0.1 2181
- 輸出相關服務配置的詳細信息
echo conf | nc 127.0.0.1 2181
- 列出所有連接到服務器的客戶端的完全的連接 / 會話的詳細信息
echo cons | nc 127.0.0.1 2181
- 輸出關于服務環(huán)境的詳細信息(區(qū)別于 conf 命令)
echo envi |nc 127.0.0.1 2181
- 列出未經(jīng)處理的請求
echo reqs | nc 127.0.0.1 2181
- 列出服務器 watch 的詳細信息
echo wchs | nc 127.0.0.1 2181
- 通過 session 列出服務器 watch 的詳細信息,它的輸出是一個與 watch相關的會話的列表
echo wchc | nc 127.0.0.1 2181
- 通過路徑列出服務器 watch 的詳細信息。它輸出一個與 session 相關的路 徑
echo wchp | nc 127.0.0.1 2181
九、Zookeeper 日志可視化
- 事務日志可視化(LogFormatter)
java -cp ../../zookeeper-3.4.6.jar;../../lib/slf4j-api-1.6.1.jar org.apache.zookeeper.server.LogFormatter log.xxxx
- 數(shù)據(jù)快照可視化( SnapshotFormatter)
java -cp ../../zookeeper-3.4.6.jar;../../lib/slf4j-api-1.6.1.jar org.apache.zookeeper.server.SnapshotFormatter snapshot.xxxx
十、Zookeeper客戶端簡介
1.Zookeeper 客戶端
zookeeper官方提供的java客戶端API;
核心API
- 創(chuàng)建會話
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly)
- 創(chuàng)建節(jié)點
public String void create(final String path, byte data[], List<ACL> acl, CreateMode createMode, StringCallback cb, Object ctx)
- 讀取數(shù)據(jù)
public List<String> void getChildren(final String path, Watcher watcher, Stat stat, Children2Callback cb, Object ctx)
public List<String> void getData(final String path, Watcher watcher, Stat stat, DataCallback cb, Object ctx)
- 更新數(shù)據(jù)
public Static void setData(final String path, byte data[], int version, StatCallback cb, Object ctx)
- 檢測節(jié)點是否存在
public Static void exists(final String path, Watcher watcher, StatCallback cb, Object ctx)
- 權限控制
public void addAuthInfo(String scheme, byte auth[])
- Watch
org.apache.zookeeper.Watcher(KeeperState、EventType)
(1)沒有專門的API去注冊watcher,依附于增刪改查API;
(2)watch是一次性產(chǎn)品
(3)watch的process方法中,可對不同事件進行處理;
原生客戶端開發(fā)弊端
- 會話的鏈接是異步的
- 序列化支持不透明
- Watch需要重復注冊
- Session 重連機制
- 開發(fā)復雜性較高
2.ZkClient
開源的zk客戶端,在原生API基礎上封裝,是一個更易于使用的zookeeper客戶端;
引入Maven依賴
<!-- zkclient依賴 -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
核心API
- 創(chuàng)建會話(同步,重試)
public ZkClient(final String zkServers, final int sessionTimeout,final int connectionTimeout, final ZkSerializer zkSerializer, final long operationRetryTimeout)
- 創(chuàng)建節(jié)點(同步、遞歸創(chuàng)建)
String create(final String path, Object data, final CreateMode mode)
public void createPersistent(String path,boolean createParents,List<ACL> acl)
public void createPersistent(String path, Object data, List<ACL> acl)
public String createPersistentSequential(String path,Object data,List<ACL> acl)
public String createPersistentSequential(String path,Object data,List<ACL> acl
public String createEphemeralSequential(String path,Object data,List<ACL> acl)
- 刪除節(jié)點(同步,遞歸調(diào)用)
public boolean delete(String path,int version)
public boolean deleteRecursive(String path)
- 獲取節(jié)點(同步,避免不存在異常)
public List<String> getChildren(String path)
public <T> T readData(String path, boolean returnNullIfPathNotExists)
public <T> T readData(String path, Stat stat)
- 更新節(jié)點(同步、實現(xiàn)CAS、狀態(tài)返回)
public void writeData(String path, Object datat, int expectedVersion)
public Stat writeDataReturnStat(String path,Object datat,int expectedVersion)
- 檢測節(jié)點存在(同步)
public boolean exists(String path)
- 權限控制(同步)
public void addAuthInfo(String scheme, final byte[] auth);
public void setAcl(final String path, final List<ACL> acl);
-
監(jiān)聽器
監(jiān)聽器
3.Cuator
開源的zk客戶端,在原生API基礎上封裝,apache頂級項目;
- Curator采用Fluent風格API
<!-- curator依賴 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
序列化支持不好
Curator 核心API
- 創(chuàng)建會話(同步、重試)
CuratorFrameworkFactory.newClient(String connectString, int sessionTimeoutM int connectionTimeoutMs, RetryPolicy retryPolicy)
CuratorFrameworkFactory.builder().connectString("192.168.11.56:2180")
.sessionTimeoutMs(30000).connectionTimeoutMs(30000)
.canBeReadOnly(false)
.retryPolicy(new ExponentialBackoffRetry(1000, Integer.MAX_VALUE))
.build();
retryPolicy 連接策略:
RetryOneTime: 只重連一次.
RetryNTime: 指定重連的次數(shù)N.
RetryUtilElapsed: 指定最大重連超時時間和重連時間間隔,間歇性重連直到超時或者鏈接成功.
ExponentialBackoffRetry: 基于"backoff"方式重連,和RetryUtilElapsed的區(qū)別是重連的時間間隔是動態(tài)
BoundedExponentialBackoffRetry: 同ExponentialBackoffRetry,增加了最大重試次數(shù)的控制.
- 創(chuàng)建節(jié)點
client.create().creatingParentIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(aclList)
.forPath(path, "hello, zk".getBytes());
- 刪除節(jié)點
client.delete().guaranteed().deletingChildrenIfNeeded()
.withVersion(version).forPath(path)
- 獲取節(jié)點
client.getData().storingStatIn(stat).forPath(path);
client.getChildren().forPath(path);
- 更新節(jié)點
client.setData().withVersion(version).forPath(path, data)
- 判斷節(jié)點是否存在
client.checkExists().forPath(path);
- 設置權限
Build.authorization(String scheme, byte[] auth)
client.setACL().withVersion(version)
.withACL(ZooDefs.Ids.CREATOR_ALL_ACL)
.forPath(path);
- 監(jiān)聽器(避免重復監(jiān)聽)
Cache是curator中對事件監(jiān)聽的包裝,對事件的監(jiān)聽可以近似看做是本地緩存視圖和遠程zk視圖的對比過程
- NodeCache 節(jié)點緩存用于處理節(jié)點本身的變化 ,回調(diào)接口NodeCacheListener
- PathChildrenCache 子節(jié)點緩存用于處理節(jié)點的子節(jié)點變化,回調(diào)接口 PathChildrenCacheListener
- TreeCache NodeCache和PathChildrenCache的結(jié)合體,回調(diào)接口TreeCacheListener
- 事務支持(保證一組操作的原子性)
Collection<CuratorTransactionResult> results = client.transaction().forOperations(operations);
- 異步支持
引入BackgroundCallback接口,用于處理異步接口調(diào)用之后服務端返回的結(jié)果信息
public void processResult(CuratorFramework client, CuratorEvent event)
CuratorEventType 事件類型
org.apache.zookeeper.KeeperException.Code
