zookeeper使用详解(命令、客户端、源码)

1. zookeeper使用详解(命令、客户端、源码)

1.1. 前言

  zookeeper咱们经常使用来作分布式协调中间件,不少时候咱们都接触不到它的原理和用法,我对他的了解也仅限于知道它能够作分布式协调、配置管理、分布式锁,而且有个watch节点监听经常能听到。接下来我要系统的学下zookeeper的功能和原理,一块儿走进zookeeper的世界html

1.2. 概述

  zookeeper主要目的就是为了分布式应用提供协同服务,zookeeper的节点管理机制,当节点发生变化时(建立、删除、数据变动),能够通知各个客户端,利用这种特性,zk的主要场景就如我前面说的:java

1. 统一配置:把配置放在ZooKeeper的节点中维护,当配置变动时,客户端能够收到变动的通知,并应用最新的配置。
2. 集群管理:集群中的节点,建立ephemeral的节点,一旦断开链接,ephemeral的节点会消失,其它的集群机器能够收到消息。
3. 分布式锁:多个客户端发起节点建立操做,只有一个客户端建立成功,从而得到锁。

1.3. zk基本操做

  1. ZNode ZNode是ZK树形结构的一个节点,它能够包含或者不包含数据。
  2. ZK提供了以下API,用于操做ZNode。
    1. create path data
    2. delete path data
    3. exists path
    4. getdata path
    5. putdata path data
    6. getChildren path
  3. ZK客户端经过创建一个Session会话,来链接ZK服务,经过这些API来操做ZNode。

1.4. zk模式

  1. ZNode模式 目前ZNode包含持久模式和短暂模式ephemeral。
  2. ephemeral模式指的是这个节点在session断了以后就会消失
  3. 持久模式和ephemeral模式外,ZNode还能够是有序的(自动分配自增ID到节点上,好比task-1,task-2,task-3)
  4. 所以ZNode一共有四种形态:
    1. 持久
    2. 持久有序
    3. ephemeral
    4. ephemeral有序

1.5. watch机制

  1. Watch和Notifications Watch能够避免每次主动去请求数据是否变化,而是当ZNode变化时,来通知。
  2. Watch是个一次性操做,每次收到通知后,必须从新watch,若是时间比较久或者ZNode更新频繁,在此时间之间,可能会有更新没有被通知到(还没来得急watch)。
  3. ZNode的建立、删除和修改均可以被watch到。

1.6. FAQ

1.6.1. 客户端对服务器列表的轮询机制

  1. 客户端创建与zk会话的时候须要咱们填写zk服务器列表,以后该列表会被随机打散而后每次请求轮询该列表,这种打散是一次性的,以后每次都是这个顺序
  2. 知道客户端轮询原理,能够知道列表是能够重复填写的,这样也能够经过重复填写配置地址来增长权重,但也会有风险,可能使server切换耗时过长,却是session_expired

参考 https://blog.51cto.com/nileader/932948node

1.6.2. 客户端常遇到的浴场Connectionloss(链接断开)和Sessionexpired(session过时)处理

  1. 在ZooKeeper中,服务器和客户端之间维持的是一个长链接,在 SESSION_TIMEOUT 时间内,服务器会肯定客户端是否正常链接(客户端会定时向服务器发送heart_beat),服务器重置下次SESSION_TIMEOUT时间。所以,在正常状况下,Session一直有效,而且zk集群全部机器上都保存这个Session信息。在出现问题状况下,客户端与服务器之间链接断了(客户端所链接的那台zk机器挂了,或是其它缘由的网络闪断),这个时候客户端会主动在地址列表(初始化的时候传入构造方法的那个参数connectString)中选择新的地址进行链接
  2. connectionloss一般发生在链接的zk挂了,这个时候只要等待客户端链接上新的zk机器(zk必须集群),而后确认操做是否执行成功
  3. sessionexpired一般发生在zk客户端和服务器的链接断了,视图连上新的zk机器,若是这个过程耗时过长,超过session_timeout时间,那么服务器认为这个session已经结束(服务器没法确认时由于其余异常缘由仍是客户端主动结束会话),开始清除和这个会话相关的信息,包括会话建立的临时节点和注册的watcher。这时客户端从新链接上服务器,服务器会报sessionexpired。这个时候解决办法要看业务状况了,只能从新实例化zk对象,从新操做节点数据

1.6.3. 建立的临时节点何时会被删除,是链接一断就删除吗?

  1. 链接断了以后,ZK不会立刻移除临时数据,只有当SESSIONEXPIRED以后,才会把这个会话创建的临时数据移除。所以,用户须要谨慎设置Session_TimeOut

1.6.4. zk日志清理

  1. zk不会自动清理日志,参考:https://blog.51cto.com/nileader/932156

1.7. zkClient命令行(包含了所有命令)

1.7.1. 建立节点

  1. 语法create [-s] [-e] path data acl
  2. -s 建立有序节点 -e建立临时节点
  3. acl专门一节讲git

    [zk: localhost:2181(CONNECTED) 4] create -s -e /mynode/subnode hellp
    Node does not exist: /mynode/subnode
    [zk: localhost:2181(CONNECTED) 5] create -s -e /mynode/ hellp       
    Node does not exist: /mynode/
    [zk: localhost:2181(CONNECTED) 6] create -s -e /mynode hellp 
    Created /mynode0000000001
    [zk: localhost:2181(CONNECTED) 7] create -s -e /mynode/subnode hello
    Node does not exist: /mynode/subnode
    [zk: localhost:2181(CONNECTED) 8] ls /mynode
    Node does not exist: /mynode
    [zk: localhost:2181(CONNECTED) 9] ls /
    [mycat, mynode0000000001, zookeeper]
    [zk: localhost:2181(CONNECTED) 10] create -s -e /mynode0000000001/subnode hello
    Ephemerals cannot have children: /mynode0000000001/subnode
  4. 上面的命令能够看出
    1. 第一不能直接建立多级几点
    2. 第二建立临时节点不能有子节点
    3. 第三有序节点节点名后会加上序号

1.7.2. 列出节点 ls

  1. ls path [watch]
  2. ls2 path [watch]
[zk: localhost:2181(CONNECTED) 17] ls2 /persistence
[]
cZxid = 0x12
ctime = Tue Mar 26 06:52:28 GMT 2019
mZxid = 0x12
mtime = Tue Mar 26 06:52:28 GMT 2019
pZxid = 0x12
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 18] ls /persistence 
[]
  1. 能够看出,ls2能给出更详细的路径信息
[zk: localhost:2181(CONNECTED) 2] ls / 1
[persistence, temporary, mycat, zookeeper]
[zk: localhost:2181(CONNECTED) 3] create -e /temp 123

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/
Created /temp
[zk: localhost:2181(CONNECTED) 4] create -e /temp2 123
Created /temp2
  1. 根节点建立监听器1,以后根节点的改变会触发监听器,但只有一次

1.7.3. 获取节点信息 get

  1. get path [watch]
[zk: localhost:2181(CONNECTED) 5] get /temp2
123
cZxid = 0x17
ctime = Tue Mar 26 06:59:20 GMT 2019
mZxid = 0x17
mtime = Tue Mar 26 06:59:20 GMT 2019
pZxid = 0x17
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x103c831d4dc0003
dataLength = 3
numChildren = 0
  1. 能够看到,get到路径有详细信息,和ls2得到的信息同样
  2. 每个对znode树的更新操做,都会被赋予一个全局惟一的ID,咱们称之为zxid(ZooKeeper Transaction ID)
  3. 更新操做的ID按照发生的时间顺序升序排序。例如,z1大于z2,那么z1的操做就早于z2操做。
  4. 每一个 znode 的状态信息包含如下内容:
    • czxid,建立(create)该 znode 的 zxid
    • mzxid,最后一次修改(modify)该 znode 的 zxid
    • pzxid,最后一次修改该 znode 子节点的 zxid
    • ctime,建立该 znode 的时间
    • mtime,最后一次修改该 znode 的时间
    • dataVersion,该节点内容的版本,每次修改内容,版本都会增长
    • cversion,该节点子节点的版本
    • aclVersion,该节点的 ACL 版本
    • ephemeralOwner,若是该节点是临时节点(ephemeral node),会列出该节点所在客户端的 session id;若是不是临时节点,该值为 0
    • dataLength,该节点存储的数据长度
    • numChildren,该节点子节点的个数

1.7.4. 检查状态 stat

  1. stat path [watch]
  2. 与 get 的区别是,不会列出 znode 的值。

1.7.5. 修改节点 set

  1. set path data [version]
  2. 修改已经存在的节点的值
[zk: localhost:2181(CONNECTED) 10] set /temp2 456
cZxid = 0x17
ctime = Tue Mar 26 06:59:20 GMT 2019
mZxid = 0x18
mtime = Tue Mar 26 07:12:27 GMT 2019
pZxid = 0x17
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x103c831d4dc0003
dataLength = 3
numChildren = 0
  1. 能够看到,在修改节点值以后,mZxid、mtime、dataVersion 都发生了变化。

1.7.6. 删除节点 rmr

  1. rmr path
[zk: localhost:2181(CONNECTED) 12] rmr /temp2
[zk: localhost:2181(CONNECTED) 13] get /temp2
Node does not exist: /temp2
  1. 删除 /mynode,不会返回任何内容。若是有子节点的时候,连带子节点也一块儿删除

1.7.7. 删除节点 delete

  1. delete path [version]
  2. 调用delete和set操做时,若是指定znode版本号,须要与当前的版本号匹配。若是版本号不匹配,操做将会失败。失败的缘由多是在咱们提交以前,该znode已经被修改过了,版本号发生了增量变化。若是不指定版本号,就是直接操做最新版本的 znode。
  3. 若是要删除的节点有子节点,不能删除
[zk: localhost:2181(CONNECTED) 17] create /per 1
Created /per
[zk: localhost:2181(CONNECTED) 18] create /per/subper 2
Created /per/subper
[zk: localhost:2181(CONNECTED) 19] delete /per
Node not empty: /per
[zk: localhost:2181(CONNECTED) 20] delete /per/subper
[zk: localhost:2181(CONNECTED) 21] delete /per
[zk: localhost:2181(CONNECTED) 22] ls /per
Node does not exist: /per

1.7.8. 历史记录 history

  1. history 列出最近的10条历史记录
[zk: localhost:2181(CONNECTED) 23] history
13 - get /temp2
14 - ls /
15 - ls /temp
16 - get /temp
17 - create /per 1
18 - create /per/subper 2
19 - delete /per
20 - delete /per/subper
21 - delete /per
22 - ls /per
23 - history

1.7.9. 重复以前的命令 redo

  1. redo cmdno 根据 cmdno 重复以前的命令,cmdno 就是方括号里面最后的数字,每次执行命令都会自增。
[zk: localhost:2181(CONNECTED) 25] redo 22
Node does not exist: /per
[zk: localhost:2181(CONNECTED) 26] redo 17
Created /per

1.7.10. 是否输出 watch 事件(printwatches)

  1. printwatches on|off
[zk: localhost:2181(CONNECTED) 28] printwatches
printwatches is on
[zk: localhost:2181(CONNECTED) 29] ls /mynode
Node does not exist: /mynode
[zk: localhost:2181(CONNECTED) 30] create /mynode 123
Created /mynode
[zk: localhost:2181(CONNECTED) 31] ls /mynode watch
[]
[zk: localhost:2181(CONNECTED) 34] create /mynode/subnode 234

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/mynode
Created /mynode/subnode
[zk: localhost:2181(CONNECTED) 35] printwatches off
[zk: localhost:2181(CONNECTED) 36] ls /mynode 2
[subnode]
[zk: localhost:2181(CONNECTED) 37] create /mynode/subnode2 567
Created /mynode/subnode2
  1. 能够看到设置off后,监听打印输出就没有了

1.7.11. 关闭链接 close

  1. close
[zk: localhost:2181(CONNECTED) 38] close 
2019-03-26 07:26:59,240 [myid:] - INFO  [main:ZooKeeper@693] - Session: 0x103c831d4dc0003 closed
[zk: localhost:2181(CLOSED) 39] 2019-03-26 07:26:59,241 [myid:] - INFO  [main-EventThread:ClientCnxn$EventThread@522] - EventThread shut down for session: 0x103c831d4dc0003
ls
Not connected
[zk: localhost:2181(CLOSED) 40] ls /
Not connected

1.7.12. 打开链接 connect

  1. connect host:port
[zk: localhost:2181(CLOSED) 42] connect
2019-03-26 07:28:18,093 [myid:] - INFO  [main:ZooKeeper@442] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@782830e
[zk: localhost:2181(CONNECTING) 43] 2019-03-26 07:28:18,096 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1029] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2019-03-26 07:28:18,097 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@879] - Socket connection established to localhost/127.0.0.1:2181, initiating session
2019-03-26 07:28:18,100 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1303] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x103c831d4dc0004, negotiated timeout = 30000

[zk: localhost:2181(CONNECTED) 43] ls /
[mycat, mynode, zookeeper, persistence, per]
  1. 能够看到默认链接的是本地的2181

1.7.13. 退出链接 quit

  1. quit 直接退出当前的 zkCli 命令行。

1.7.14. 强制同步 sync

  1. sync path
  2. sync方法会强制客户端所链接的服务器状态与leader的状态同步,这样在读取 path 的值就是最新的值了

1.7.15. ACL 操做

  1. 一个znode中不只包含了存储的数据,还有 ACL(Access Control List)。znode的建立时,能够给它设置一个ACL(Access Control List),来决定谁能够对znode作哪些操做
  2. ACL 具备如下特色:
    • ZooKeeper的权限控制是基于每一个znode节点的,须要对每一个节点设置权限
    • 每一个znode支持设置多种权限控制方案和多个权限
    • 子节点不会继承父节点的权限,客户端无权访问某节点,但可能能够访问它的子节点
    • 因此任何一个客户端均可以经过exists 操做来得到任何znode的状态,从而得知znode是否真的存在。

1.7.16. ACL Permissions

ACL 权限 ACL 简写 容许的操做
CREATE c 建立子节点
READ r 获取节点的数据和它的子节点
WRITE w 设置节点的数据
DELETE d 删除子节点 (仅下一级节点)
ADMIN a 设置 ACL 权限

1.7.17. 权限相关命令

命令 语法 描述
getAcl getAcl path 读取ACL权限
setAcl setAcl path acl 设置ACL权限
addauth addauth scheme auth 添加认证用户
create create [-s] [-e] path data acl 建立节点时指明 ACL 权限

1.7.18. ACL Schemes

  1. ZooKeeper内置了一些权限控制方案,能够用如下方案为每一个节点设置权限:
方案 描述
world 只有一个用户:anyone,表明全部人(默认)
ip 使用IP地址认证
auth 使用已添加认证的用户认证
digest 使用“用户名:密码”方式认证
  1. ACL是由鉴权方式、鉴权方式的ID和一个许可(permession)的集合组成。例如,咱们想经过一个ip地址为10.0.0.1的客户端访问一个znode。那么,咱们须要为znode设置一个ACL,鉴权方式使用IP鉴权方式,鉴权方式的ID为10.0.0.1,只容许读权限。那么 ACL 的格式就是:ip:10.0.0.1:w

1.7.18.1. world 方案

  1. setAcl <path> world:anyone:<acl>
[zk: localhost:2181(CONNECTED) 7] getAcl /world
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 8] setAcl /world world:anyone:cdr
cZxid = 0x27
ctime = Tue Mar 26 07:41:43 GMT 2019
mZxid = 0x27
mtime = Tue Mar 26 07:41:43 GMT 2019
pZxid = 0x27
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0
[zk: localhost:2181(CONNECTED) 9] set /world 234
Authentication is not valid : /world
  1. 能够看出,在修改权限为 cdr 以后,不能再设置节点数据了。注意 aclVersion 也发生了变化

1.7.18.2. IP 方案

  1. setAcl <path> ip:<ip>:<acl>
[zk: localhost:2181(CONNECTED) 10] create /ip hello
Created /ip
[zk: localhost:2181(CONNECTED) 11] getAcl /ip    
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 12] setAcl /ip ip:52.231.163.100:cdrwa
cZxid = 0x2a
ctime = Tue Mar 26 07:56:21 GMT 2019
mZxid = 0x2a
mtime = Tue Mar 26 07:56:21 GMT 2019
pZxid = 0x2a
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 13] getAcl /ip                        
'ip,'52.231.163.100
: cdrwa
[zk: localhost:2181(CONNECTED) 14] get /ip
Authentication is not valid : /ip

1.7.18.3. auth 方案

addauth digest <user>:<password> #添加认证用户
setAcl <path> auth:<user>:<acl>
[zk: localhost:2181(CONNECTED) 15] create /auth hello
Created /auth
[zk: localhost:2181(CONNECTED) 16] addauth digest admin:tom
[zk: localhost:2181(CONNECTED) 17] setAcl /auth auth:tom:cdrwa
cZxid = 0x2c
ctime = Tue Mar 26 08:04:23 GMT 2019
mZxid = 0x2c
mtime = Tue Mar 26 08:04:23 GMT 2019
pZxid = 0x2c
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 18] getAcl /auth
'digest,'admin:cFk4QI8k/ZVgHVEnb06Vtoc651o=
: cdrwa

断开之后再连上,须要从新认证github

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] get /auth
Authentication is not valid : /auth

1.7.18.4. digest 方案

  1. setAcl <path> digest:<user>:<password>:<acl>
  2. 这里的密码是通过SHA1及BASE64处理的密文,在SHELL中能够经过如下命令计算:算法

    echo -n : | openssl dgst -binary -sha1 | openssl base64 apache

[root@izbp1itlw36onyj4m9b4hiz ~]# echo -n admin:123456 | openssl dgst -binary -sha1 | openssl base64
0uek/hZ/V9fgiM35b0Z2226acMQ=
[zk: localhost:2181(CONNECTED) 21] create /digest hello
Created /digest
[zk: localhost:2181(CONNECTED) 22] setAcl /digest digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:cdrw 
cZxid = 0x39
ctime = Tue Mar 26 08:22:04 GMT 2019
mZxid = 0x39
mtime = Tue Mar 26 08:22:04 GMT 2019
pZxid = 0x39
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 23] getAcl /digest
'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ=
: cdrw
[zk: localhost:2181(CONNECTED) 24] get /digest
Authentication is not valid : /digest
[zk: localhost:2181(CONNECTED) 25] addauth digest admin:123456
[zk: localhost:2181(CONNECTED) 26] get /digest
hello
cZxid = 0x39
ctime = Tue Mar 26 08:22:04 GMT 2019
mZxid = 0x39
mtime = Tue Mar 26 08:22:04 GMT 2019
pZxid = 0x39
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

1.7.18.5. 建立节点时指定 ACL

[zk: localhost:2181(CONNECTED) 27] addauth digest admin:tim
[zk: localhost:2181(CONNECTED) 28] create /createnode hello auth:tim:cdrwa  
Created /createnode
[zk: localhost:2181(CONNECTED) 29] getAcl
[zk: localhost:2181(CONNECTED) 30] getAcl /createnode
'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ=
: cdrwa
'digest,'admin:H4JbicQawMpoqvA2LI0LFNFSMNE=
: cdrwa
[zk: localhost:2181(CONNECTED) 31] get /createnode
hello
cZxid = 0x3b
ctime = Tue Mar 26 08:27:27 GMT 2019
mZxid = 0x3b
mtime = Tue Mar 26 08:27:27 GMT 2019
pZxid = 0x3b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 32] close

[zk: localhost:2181(CLOSED) 33] connect
[zk: localhost:2181(CONNECTED) 34] get /createnode
Authentication is not valid : /createnode
[zk: localhost:2181(CONNECTED) 35] getAcl /createnode
'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ=
: cdrwa
'digest,'admin:H4JbicQawMpoqvA2LI0LFNFSMNE=
: cdrwa
[zk: localhost:2181(CONNECTED) 36] addauth digest admin:tim
[zk: localhost:2181(CONNECTED) 37] get /createnode
hello
cZxid = 0x3b
ctime = Tue Mar 26 08:27:27 GMT 2019
mZxid = 0x3b
mtime = Tue Mar 26 08:27:27 GMT 2019
pZxid = 0x3b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
  1. 关闭会话后须要从新受权

1.7.19. zookeeper quota

  1. zookeeper quota 机制支持节点个数(namespace)和空间大小(bytes)的设置。
  2. zookeeper quota 保存在 /zookeeper/quota 节点下,能够设置该节点的 ACL 权限,以防其余人修改。
  3. 语法listquota pathsetquota -n|-b val pathdelquota [-n|-b] path
  4. -n表示设置znode count限制,包含自身节点
[zk: localhost:2181(CONNECTED) 10] ls /zookeeper/quota
[]
[zk: localhost:2181(CONNECTED) 11] get /zookeeper/quota

cZxid = 0x0
ctime = Thu Jan 01 00:00:00 GMT 1970
mZxid = 0x0
mtime = Thu Jan 01 00:00:00 GMT 1970
pZxid = 0x0
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 0
[zk: localhost:2181(CONNECTED) 12] listquota /persistence
absolute path is /zookeeper/quota/persistence/zookeeper_limits
quota for /persistence does not exist.
[zk: localhost:2181(CONNECTED) 13] setquota -n 3 /persistence
Comment: the parts are option -n val 3 path /persistence
[zk: localhost:2181(CONNECTED) 14] create /persistence/node1 123
Created /persistence/node1
[zk: localhost:2181(CONNECTED) 15] create /persistence/node2 124
Created /persistence/node2
[zk: localhost:2181(CONNECTED) 16] create /persistence/node3 125
Created /persistence/node3
[zk: localhost:2181(CONNECTED) 17] listquota /persistence
absolute path is /zookeeper/quota/persistence/zookeeper_limits
Output quota for /persistence count=3,bytes=-1
Output stat for /persistence count=4,bytes=12
  1. 能够看到,超出节点数zk也不会报错,只会在listquato里打下日志
[zk: localhost:2181(CONNECTED) 18] delquota -n /persistence
[zk: localhost:2181(CONNECTED) 19] listquota /persistence  
absolute path is /zookeeper/quota/persistence/zookeeper_limits
Output quota for /persistence count=-1,bytes=-1
Output stat for /persistence count=4,bytes=12
  1. 删除节点quota,count变成-1

1.8. java操做

1.8.1. 异步操做

  1. 主流异步操做api

    参考 https://github.com/llohellohe/zookeeper/blob/master/src/main/java/yangqi/zookeeper/example/masterworker/AsynMaster.java服务器

1.8.2. 状态变动

  1. Watcher优点 经过watcher,能够避免主动轮询致使的额外负担,更加实时和有效率。
  2. Watcher接口,仅有一个实现接口public void process(WatchedEvent event)
  3. WatchedEvent表明watcher到的事件,它包含发生了什么事件,ZooKeeper的当前链接状态以及产生事件的ZNode路径
    1. KeeperState
    2. EventType
    3. path
  4. KeeperState包含Disconnected\SyncConnected\AuthFailed\ConnectedReadOnly\SaslAuthenticated\Expired等6种状态。
  5. EventType包含五种状态:
    1. None
    2. NodeCreated
    3. NodeDeleted
    4. NodeDataChanged
    5. NodeChildrenChanged
    • 其中后四种用于表示ZNode的状态或者数据变动,而None则用于会话的状态变动。
  6. EventType为None的Watch SessionWatch实例描述了,初始化一个ZooKeeper实例时注册的Watcher接口
    1. 将在链接时收到EventType为None,KeeperState为SyncConnected,path为null的Event
    2. 将在失去链接时收到EventType为None,KeeperState为:Disconnected,path为null的Event
  7. ChildrenCallback 经过getChildren方法,能够设置ChildrenCallback,以便得到得到当子节点发生变化时的相关信息。
    • ChildrenCallback 的惟一接口:
    public void processResult(int rc, String path, Object ctx, List<String> children)
    • getChildren能够设置对应的Watcher,一旦发现节点的事件类型为NodeChildrenChanged后,能够继续设置watch
  8. 例子:https://github.com/llohellohe/llohellohe.github.com/blob/master/readers/ZooKeeper/04-%E7%8A%B6%E6%80%81%E5%8F%98%E6%9B%B4.md
  9. 上述这篇例子我用zookeeper3.4.13运行会报NullPointException,我dubug后发现,该版本zookeeper在进入连接刚创建状态(None)时,会添加默认defaultWatcher,而示例代码初始化时,watch填了null,致使了后续,从set中获得的watcher为null,我认为这是个bug,初始化链接不放watch应该也是容许的。
@Override
        public Set<Watcher> materialize(Watcher.Event.KeeperState state,
                                        Watcher.Event.EventType type,
                                        String clientPath)
        {
            Set<Watcher> result = new HashSet<Watcher>();

            switch (type) {
            case None://初始化链接时进入
                result.add(defaultWatcher);//defaultWatcher若是填空,后续会报错
private void processEvent(Object event) {
          try {
              if (event instanceof WatcherSetEventPair) {
                  // each watcher will process the event
                  WatcherSetEventPair pair = (WatcherSetEventPair) event;
                  for (Watcher watcher : pair.watchers) {
                      try {
                          watcher.process(pair.event);//watcher为null
  1. processEvent方法就是EvenetThread线程处理watcher监听的地方

1.8.3. Curator

  1. Curator是构建在ZooKeeper上的API,它屏蔽一些复杂的ZooKeeper操做,并提供了一些扩展。

1.8.3.1. 流式API

  1. 通常的ZooKeeper建立节点的代码以下:
zk.create("/mypath", new byte[0],
              ZooDefs.Ids.OPEN_ACL_UNSAFE,
              CreateMode.PERSISTENT);

1.8.3.2. 使用Curator后

zkc.create().withMode(CreateMode.PERSISTENT).forPath("/mypath", new byte[0]);

1.8.3.3. leader latch 和leader selection

  • 经过Curator能够方便的进行leader的选举和控制

1.8.3.4. Leader Elections

  在ZK集群中,Leader的做用是保证变动操做(create\setData\delete)的顺序性
它将接收到的请求转换成事务,而后提议followers按照顺序应用这些事务。在初始阶段,全部的ZK服务端都处于LOOKING状态,要么找到已经存在Leader结点,要么本身选举出Leader。成为Leader的节点将进入LEADING状态,其它则将进入FOLLOWING状态网络

1.8.3.5. 选举过程

  1. 进入LOOKING状态的server将广播消息,称为vote。
  2. vote包含serverId和ZXID,好比(1,5)表示server id为1的服务端,它的ZXID为5。
  3. 每一个server将比较本身的vote和收到的vote,若是:
    1. 收到vote的zxid大于本身的,则使用这个vote
    2. 若是zxid相等,则sid大的获胜
    3. 当推选超过半数以上,则肯定leader

1.9. 源码分析

1.9.1. 客户端组成

  • Zookeeper客户端主要由以下核心部件构成。
    1. Zookeeper实例,客户端入口
    2. ClientWatchManager, 客户端Watcher管理器
    3. HostProvider,客户端地址列表管理器。
    4. ClientCnxn,客户端核心线程,内部包含了SendThread和EventThread两个线程,SendThread为I/O线程,主要负责Zookeeper客户端和服务器之间的网络I/O通讯;EventThread为事件线程,主要负责对服务端事件进行处理。
      1

1.9.2. 初始化

  • 客户端在初始化和启动过程当中大致能够分为以下3个步骤
    1. 设置默认Watcher
    2. 设置Zookeeper服务器地址列表
    3. 建立ClientCnxn。
  • 若在Zookeeper构造方法中传入Watcher对象时,那么Zookeeper就会将该Watcher对象保存在ZKWatcherManager的defaultWatcher中,并做为整个客户端会话期间的默认Watcher。

1

1.9.3. 会话的建立

  1. 客户端与服务端会话创建的整个过程,包括初始化阶段(第一阶段)、会话建立阶段(第二阶段)、响应处理阶段(第三阶段)三个阶段。
    2
    细节:
    1

1.9.4. 服务器地址列表

  1. 在实例化Zookeeper时,用户传入Zookeeper服务器地址列表,如192.168.0.1:2181,192.168.0.2:2181,192.168.0.3:2181
    • Chroot:客户端能够设置本身的命名空间,若客户端设置了Chroot,此时,该客户端对服务器的任何操做都将被限制在本身的命名空间下,如设置Choot为/app/X,那么该客户端的全部节点路径都是以/app/X为根节点。具体设置可使输入地址列表的时候加上192.168.0.1:2181/app/X
    • 地址列表管理:Zookeeper使用StaticHostProvider打散服务器地址(shuffle),并将服务器地址造成一个环形循环队列,而后再依次取出服务器地址。

1.10. 总结

  1. zookeeper比较重要的概念就是选主算法,这里尽量简单的简述一下,分两类:
    • 全新集群选主:按照服务器启动顺序,判断server_id大小,根据过半选举的规则选主;好比服务器1-5对应server_id1-5,按顺序启动,每启动一台会有个选主过程,服务器会交换选主信息,id大的胜出,启动2台时,因为没有超过半数以上的机器,因此继续保持LOOKING,当第三台机器启动,id最大,且选票结果超过半数,则肯定leader为server_id=3的机器,后续四、5启动,因为已经存在leader,只能当following
    • 非全新集群选主:这种状况说明zookeeper集群leader机器宕机,须要从新选举,须要根据数据的票选轮数epoch、zxid和server_id判断,先进行选举信息的交换,票选轮数小的忽略,zxid大的胜出,zxid相同状况看server_id,server_id大的胜出
    • zookeeper3.4.13查看源码能够发现,protected int electionAlg = 3;
    case 3:
            qcm = createCnxnManager();
            QuorumCnxManager.Listener listener = qcm.listener;
            if(listener != null){
                listener.start();
                le = new FastLeaderElection(this, qcm);
            } else {
                LOG.error("Null listener when initializing cnx manager");
            }
            break;

参考

https://blog.csdn.net/feixiang2039/article/details/79810102#zookeeper-%E5%91%BD%E4%BB%A4 zookeeper 客户端 zkCli 命令详解

https://github.com/llohellohe/llohellohe.github.com/blob/master/readers/ZooKeeper llohellohe/llohellohe.github.com

http://www.cnblogs.com/leesf456/p/6098255.html Zookeeper客户端

https://blog.csdn.net/cxhzqhzq/article/details/6568040 Zookeeper全解析——Paxos做为灵魂

http://blog.chinaunix.net/uid-26726125-id-4038581.html zookeeper跟经典paxos的对比(附源码)

https://blog.csdn.net/panxj856856/article/details/80403487 FastLeaderElection选举算法

相关文章
相关标签/搜索