zookeeper基础教程

一、关于zookeeper

Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储, Zookeeper 作用主要是用来维护和监控存储的数据的状态变化,通过监控这些数据状态的变化,从而达到基于数据的集群管理。

简单的说,zookeeper=文件系统+通知机制。

1. ZNode节点

ZNode被分为持久(persistent)节点,顺序(sequential)节点和临时(ephemeral)节点。

  • 持久节点:即使在创建该特定znode的客户端断开连接后,持久节点仍然存在。默认情况下,除非另有说明,否则所有znode都是持久的。
  • 顺序节点:顺序节点可以是持久的或临时的。当一个新的znode被创建为一个顺序节点时,ZooKeeper通过将10位的序列号附加到原始名称来设置znode的路径。例如,如果将具有路径 /test 的znode创建为顺序节点,则ZooKeeper会将路径更改为 /test0000000001 ,并将下一个序列号设置为0000000002。如果两个顺序节点是同时创建的,那么ZooKeeper不会对每个znode使用相同的数字。顺序节点在锁定和同步中起重要作用。
  • 临时节点:客户端活跃时,临时节点就是有效的。当客户端与ZooKeeper集合断开连接时,临时节点会自动删除。因此,只有临时节点不允许有子节点。如果临时节点被删除,则下一个合适的节点将填充其位置。临时节点在leader选举中起着重要作用。

2. Session回话

会话对于ZooKeeper的操作非常重要。会话中的请求按FIFO顺序执行。一旦客户端连接到服务器,将建立会话并向客户端分配会话ID 

客户端以特定的时间间隔发送心跳以保持会话有效。如果ZooKeeper集合在超过服务器开启时指定的期间(会话超时)都没有从客户端接收到心跳,则它会判定客户端死机。

会话超时通常以毫秒为单位。当会话由于任何原因结束时,在该会话期间创建的临时节点也会被删除。

3.Watcher监视

监视是一种简单的机制,使客户端收到关于ZooKeeper集合中的更改的通知。客户端可以在读取特定znode时设置Watches。Watches会向注册的客户端发送任何znode(客户端注册表)更改的通知。

Znode更改是与znode相关的数据的修改或znode的子项中的更改。只触发一次watches。如果客户端想要再次通知,则必须通过另一个读取操作来完成。当连接会话过期时,客户端将与服务器断开连接,相关的watches也将被删除。

二、Zookeeper客户端命令

执行bin/zkCli.sh连接zookeeper服务器后,我们随便敲一个aa然后回车,可以看到支持如下命令:

stat path [watch] --- 查看节点状态
set path data [version]  --- 设置节点数据
ls path [watch]  --- 列出子节点
delquota [-n|-b] path --- 删除节点配额
ls2 path [watch]  --- 列出子节点,并显示当前节点信息
setAcl path acl  --- 设置节点权限
setquota -n|-b val path  --- 设置节点配额
history  --- 列出历史命令
redo cmdno  --- 执行历史命令 
printwatches on|off
delete path [version] --- 删除节点,如果待删除节点包含子节点则删除失败
sync path
listquota path  --- 列出节点配额
get path [watch] --- 获取节点数据
create [-s] [-e] path data acl --- 创建子节点
addauth scheme auth  --- 增加zookeeper用户认证
quit --- 退出zkCli
getAcl path  --- 查询节点权限
close --- 关闭当前连接
connect host:port --- 连接zkServer

1.  ls

此命令用于列出和显示znode的子项。ls命令要求路径以/开头,并支持tab键自动补全。例如:

  • 执行:ls /                        输出[dubbo, task, zookeeper, test, disconf]: 说明根节点下包含5个子节点。
  • 执行:ls /test                 输出[test1-2, test1-1]:说明/test节点下面有2个节点。
  • 执行:ls /test/test1-2     输出[]:说明/test/test1-2节点下面没有子节点。

2.  ls2

此命令也用于列出和显示znode的子项,与ls命令不同的是,该命令同时列出该节点的time、version等信息。例如:

执行 ls /test , 输出  

[test1-2, test1-1]
cZxid = 0x69043
ctime = Mon Sep 03 06:37:21 CST 2018
mZxid = 0x69043
mtime = Mon Sep 03 06:37:21 CST 2018
pZxid = 0x69045
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 2

3.  create

命令格式: create [-s] [-e] path data acl

  • -s:创建顺序节点。当一个新的znode被创建为一个顺序节点时,ZooKeeper通过将10位的序列号附加到原始名称来设置znode的路径。
  • -e:创建临时节点。客户端活跃时,临时节点就是有效的。当客户端与ZooKeeper集合断开连接时,临时节点会自动删除。
  • path:待创建的节点路径。以/开头。
  • data:带创建的节点存储的数据内容。
  • acl:权限控制。后面再说。

例如:

  • 创建永久节点:create /test/test1-3 "nodeData"
  • 创建临时节点:create -e /test/test1-4 "nodeData"
  • 创建循序节点:create -s /test/test1-5 "nodeData"

执行完以上命令以后,再执行 ls /test,可以看到输出结果:[test1-4, test1-2, test1-3, test1-1, test1-50000000004],可以看到创建的顺序节点test1-5被增加了序列号。我们执行quit退出zkCli,再重新执行bin/zkCli.sh重新连接,执行 ls /test,输出结果:[test1-2, test1-3, test1-1, test1-50000000004],可以看到创建的临时节点test1-4已经没有了。

4.  get

命令格式: get path [watch]。它返回znode的关联数据和指定znode的元数据。你将获得信息,例如上次修改数据的时间,修改的位置以及数据的相关信息。

  • path:节点路径。以/开头。
  • watch:监控。后面再说。

例如:执行 get /test/test1-3 输出

"nodeData"
cZxid = 0x69046
ctime = Mon Sep 03 07:00:25 CST 2018
mZxid = 0x69046
mtime = Mon Sep 03 07:00:25 CST 2018
pZxid = 0x69046
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0

5.  set

命令格式 set path data [version]。设置指定znode的数据。

  • path:节点路径。以/开头。
  • data:节点数据内容。
  • version: 当前数据版本号。如果该参数和当前数据版本号不一致则会抛异常。

例如:执行 set /test/test1-3 "dataNode-New",然后我们再执行 get /test/test1-3 查看数据,输出:

"dataNode-New"
cZxid = 0x69046
ctime = Mon Sep 03 07:00:25 CST 2018
mZxid = 0x6904b
mtime = Mon Sep 03 07:42:46 CST 2018
pZxid = 0x69046
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0

可以看到dataVersion已经发生了改变,当前 dataVersion = 1。

6.  delete

命令格式:delete path [version]。删除指定节点,如果要删除的节点包含子节点则会删除失败。

  • path:节点路径。以/开头。
  • version: 当前数据版本号。如果该参数和当前数据版本号不一致则会抛异常。

例如:执行 delete /test/test1-3,再执行 ls /test,输出:[test1-2, test1-1, test1-50000000004],可以看到test1-3这个节点已经被删除掉了。

7.  addauth

命令格式:addauth scheme auth。该命令为zookeeper增加一个用户认证,例如:addauth digest zookeeper:zookeeper-pass。其中zookeeper为用户名,zookeeper-pass为密码。

8.  history

命令格式:history。该命令用来查看历史命令,查询到的历史命令前会有编号,提供redo操作。

9.  redo

命令格式:redo cmdno。再次执行历史命令,配合history命令使用。参数cmdno即为history命令执行结果中的编号

三、Zookeeper权限控制

zookeeper支持的命令中,有如下两条命令是和权限相关的:

  • setAcl path acl
  • getAcl path
  • create [-s] [-e] path data acl

zookeeper共支持5中权限,分别是

  • create:创建节点
  • delete:删除节点
  • read:读取节点数据以及子节点
  • write:修改节点数据
  • admin:设置节点权限

zookeeper的ACL,可以从三个维度来理解:一是scheme;二是user;三是permission。通常表示为scheme:id:permissions。

授权模式

  • world: 表示所有,创建节点时默认此权限范围。有唯一的id是anyone。例如:setAcl /test/test1-1 world:anyone:cdrwa。表示/test/test1-1节点所有人都可以执行cdrwa操作。
  • auth:它不需要id,只要是通过认证的user都有权限。例如:setAcl /test/test1-2 auth:zookeeper:zookeeper-pass:cdrwa表示通过密码验证的用户名为zookeeper的用户可执行cdrwa操作。当然,执行此命令的前提是已经通过addauth命令添加了该用户名密码的用户。执行完setAcl命令后,再执行getAcl /test/test1-2,返回输出结果:
    'digest,'zookeeper:hdtnoeyerDQpPGLJ41lW1u1lCSA=
    : cdrwa
    请注意上面输出结果的hdtnoeyerDQpPGLJ41lW1u1lCSA=,该值紧接着我们就会用到。
  • digest:类似于auth授权。与auth授权的区别在于,输入命令的时候,用户密码需要先进行SHA-1加密再进行Base64编码。首先,先生成加密后的密码,执行:echo -n zookeeper:zookeeper-pass | openssl dgst -binary -sha1 | openssl base64,输出:hdtnoeyerDQpPGLJ41lW1u1lCSA=。可以看到该输出值即为auth授权后重新getAcl得到的值。然后,执行digest授权:setAcl /test/test1-3 digest:zookeeper:hdtnoeyerDQpPGLJ41lW1u1lCSA=:cdrwa。通过 getAcl /test/test1-3 可以看到授权结果和 getAcl /test/test1-2完全一致
  • ip:它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如执行:setAcl /test/test1-4 ip:192.168.1.0/16:cdrwa, 表示匹配前16个bit的IP段。

 四、quota配额

zookeeper可以在znode上设置配额限制。如果超出了配置限制,zookeeper将会在log日志中打印WARN日志。如果超出配额限制,并不会停止行为操作。

1.  setquota

命令格式:setquota -n|-b val path。为节点设定配额。例如命令:setquota -n 10 /test/test1-1

  • -n :限制子节点个数
  • -b :限制节点数据长度
  • val :  限制的具体的值
  • path : 要做限制的节点路径

2.  delquota

命令格式:delquota [-n|-b] path。删除节点配额。例如命令:delquota  -n /test/test1-1

  • -n|-b : 要删除的配额类型
  • path : 要做限制的节点路径

3.  listquota

命令格式:listquota path。列出节点设置的配额。例如命令:listquota  /test/test1-1

五、zookeeper in java

@Test
public void testClient() throws IOException, InterruptedException, KeeperException {
    final String zookeeperHost = "10.5.31.155";
    final String zookeeperPort = "2181";
    // 链接zk服务器
    final CountDownLatch countDownLatchConnect = new CountDownLatch(1);
    final ZooKeeper zooKeeper = new ZooKeeper(zookeeperHost + ":" + zookeeperPort, 60000, event -> {
        if (event.getState().equals(Watcher.Event.KeeperState.SyncConnected)) {
            countDownLatchConnect.countDown();
        }
    });
    countDownLatchConnect.await();

    // watch节点 并执行创建
    zooKeeper.exists("/clientTestNode", System.out::println);
    zooKeeper.create("/clientTestNode", "clientTestNodeData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

    // watch节点 并执行删除
    zooKeeper.exists("/clientTestNode", System.out::println);
    zooKeeper.delete("/clientTestNode", -1);

    // 1. 创建节点 2. watch节点数据 3. 修改节点数据 4. 删除节点
    zooKeeper.create("/clientTestNode", "clientTestNodeData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    byte[] dataOld = zooKeeper.getData("/clientTestNode", System.out::println, null);
    System.out.println(new String(dataOld));
    zooKeeper.setData("/clientTestNode", "clientTestNodeDataNew".getBytes(), -1);
    byte[] dataNew = zooKeeper.getData("/clientTestNode", System.out::println, null);
    System.out.println(new String(dataNew));
    zooKeeper.delete("/clientTestNode", -1);

    // 1. 创建节点 2. 监控节点的子节点 3. 创建子节点 4. 监控节点子节点 5. 删除子节点
    zooKeeper.create("/clientTestNode", "clientTestNodeData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.create("/clientTestNode/child1", "child1Data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.create("/clientTestNode/child2", "child2Data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.delete("/clientTestNode/child1", -1);
    zooKeeper.getChildren("/clientTestNode", System.out::println, null);
    zooKeeper.delete("/clientTestNode/child2", -1);
    zooKeeper.delete("/clientTestNode", -1);

}

需要注意的点:

  1. 由于zookeeper的连接是异步过程,所以执行new Zookeeper之后不要紧接着执行zookeeper命令。否则可能会出现丢失连接异常。
  2. 由于zookeeper的watcher是一次性有效(即zookeeper回调watcher后该watcher即被删除),所以会出现重复注册watcher的现象
原文地址:https://www.cnblogs.com/LOVE0612/p/9579751.html