Zookeeper进阶及客户端框架使用

本文主要讲述ZooKeeper中的权限管理机制、Watch以及Zookeeper的客户端框架使用。

ACL

默认情况下,所以客户端能都可以读写任何节点,但有时我们需要控制节点的访问权限,ZK通过ACL机制来解决节点的访问权限问题。

  • ZooKeeper的权限控制是基于单个znode节点的,需要对每个节点设置权限
  • 每个znode支持设置多种权限控制方案和多个权限
  • 子节点不会继承父节点的权限,客户端无权访问某节点,但可以访问它的子节点。

ACL权限控制,使用schema:id:permission来标识,主要涵盖3个方面:

  • schema: 权限模式,又称鉴权策略

    • world: 只有一个用户:anyone,代表所有人(默认)
    • ip: 使用IP地址认证
    • auth: 使用已添加认证的用户认证
    • digest: 使用“用户名:密码”方式认证
  • id: 授权对象,指权限赋予的用户或者一个实体。sheema与id的对应关系:

    • world: 只有一个id,即anyone
    • ip: 通常是一个ip地址或地址段,比如192.168.0.1192.168.0.1/2
    • auth: 用户名
    • digest: 自定义:通常是“username:BASE64(SHA-1(username:password))”
  • permission: 权限

    • CREATE,简写为c,可以创建子节点
    • DELETE,简写为d,可以删除子节点
    • READ,简写为r,可以读取节点数据及显示子节点列表
    • WRITE,简写为w,可以设置节点数据
    • ADMIN,简写为a,可以设置节点访问控制列表

    这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限

查看ACL
查看/zk_test节点权限
get ACL
默认创建的节点的权限是最开放的,也就是world:anyone:cdrwa

设置ACL
设置节点对所有人都有写和管理权限
setAcl
在上下文中添加一个用户:

add auth user1:12345

设置/acl_node只有user1可以操作

setAcl /acl_node auth:user1:12345:rdwca

其他用户再访问/acl_node会报权限不可用
getacl

Watch

一个Zookeeper的节点可以被监控,包括这个目录中存储的数据的修改,子节点目录的变化,一旦变化就会通知设置监控的客户端,通过这个特性可以实现配置的集中管理、集群管理、分布式锁等。

一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生改变的时候,则服务器会将这个改变发送给设置了Watch的客户端。

一个Watcher实例是一个回调函数,被回调一次就被移除了。如果还需要关注数据的变化,需要再次注册Watcher。New Zookeeper()时注册的watcher叫Default Watcher,事件类型为 EventType.None,它不是一次性的,只对client连接状态变化做出反应。

可以注册watcher的方法:getDataexistsgetChildren

可以触发watcher的方法:createdeletesetData,连接断开的情况下触发的watcher会丢失。

event For “/path”event For “/path/child”
create("/path")EventType.NodeCreated
delete("/path")EventType.NodeDeleted
setData("/path")EventType.NodeDataChanged
create("/path/child")EventType.NodeChildrenChanged(getChild)EventType.NodeCreated
delete("/path/child")EventType.NodeChildrenChanged(getChild)EventType.NodeDeleted
setData("/path/child")EventType.NodeDataChanged

ZooKeeper客户端框架

为了作比较,我们先来演示下原生客户端的使用

原生客户端
public class ZookeeperClientTest {
    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        // zk连接
        ZooKeeper client = new ZooKeeper("localhost:2181", 5000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("Zookeeper连接: " + event);
            }
        });

        // 创建节点
        client.create("/data", "1".getBytes(), Collections.singletonList(new ACL(ZooDefs.Perms.ALL, new Id("world", "anyone"))),
                CreateMode.PERSISTENT);

        // 获取节点数据,并监控数据改变
        // 传入stat用户获取节点完整信息,比如 cZxid,ctime,cversion等
        Stat stat = new Stat();
        client.getData("/data", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (Event.EventType.NodeDataChanged.equals(event.getType())) {
                    System.out.println("数据改变");
                }
            }
        }, stat);

        // 修改节点 需要指定修改的的节点版本
        client.setData("/data", "4".getBytes(), -1);

        // 使用回调函数,用于异步方式获取结果数据
//        client.getData("/data", false, new AsyncCallback.DataCallback() {
//            @Override
//            public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
//                System.out.println("回调函数" + new String(data));
//            }
//        }, null);

//       client.delete("/data", -1);
        // 让进程等待,不会立刻退出
        System.in.read();
    }
}

这里只演示基础操作,其它操作请读者自行尝试,API清晰可读。

原生客户端存在的问题:

  • watcher是一次性的
    watch事件是一个一次性触发器,想继续使用的话需要编写额外代码,重新注册。
  • session超时问题
    客户端在连接服务端时会设置一个sessionTimeout,用来标识session过期时间,客户端会定时向服务端发送心跳以刷新服务端的session过期时间。一旦客户端和服务端之间断开连接,session就会进行倒计时,超过了过期时间就会发送Session过期,即使客户端后面又重新连接上服务端,也只能接受session过期的事件,从而删除临时节点和watcher等等。
  • API接口,节点数据需要转换为二进制数据保存。
zkclient客户端

zkclient是github上的开源项目
源码地址

项目中需要引入MAVEN依赖

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.11</version>
</dependency>

zkClient对于原生客户端存在的几个问题,都提供了自己的解决方案:

  • 解决了watcher的一次性注册问题,将znode的事件重新定义为子节点的变化、数据的变化、连接及状态的变化这三类。由zkClient统一将watcher的WatchedEvent转换到以上三种情况去处理。watcher执行后重新读取数据的同时,将再注册相同的watcher。
  • zkClient在发生session expire异常时会自动创建新的ZooKeeper实例重连。这时所有的watcher和EPHEMERAL节点都将失效,可以在zkClient定义的连接状态变化的接口 IzkStateListener里面的handleNewSession方法中进行相应的处理。
  • zkClient提供了ZKSerializer接口,可进行序列化和反序列化操作。
public class ZkClientTest {
    public static void main(String[] args) throws IOException {
        // 连接ZK
        ZkClient zk = new ZkClient("10.40.231.95:2181", 5000, 5000, new SerializableSerializer());

        // 创建节点
        zk.createPersistent("/zkclientdata", "1");

        // 创建子节点
        zk.create("/zkclientdata/children", "1", CreateMode.PERSISTENT);

        // 订阅数据变化
        zk.subscribeDataChanges("/zkclientdata", new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {
                System.out.println("数据被修改了");
            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                System.out.println("数据被删除了");
            }
        });

        // 订阅子节点变化
        zk.subscribeChildChanges("/zkclientdata", new IZkChildListener() {
            @Override
            public void handleChildChange(String s, List<String> list) throws Exception {
                System.out.println("子节点数据变化了");
            }
        });

        // 订阅状态变化
        zk.subscribeStateChanges(new IZkStateListener() {
            @Override
            public void handleStateChanged(Watcher.Event.KeeperState keeperState) throws Exception {
                System.out.println("状态变化");
            }

            @Override
            public void handleNewSession() throws Exception {
                // session失效,重建session时触发
                // 可以在这里恢复数据,比如session失效后导致的临时节点被删除
                System.out.println("session重建");
                zk.create("/zkclientdata-ls", "4", CreateMode.EPHEMERAL);
            }
        });
        
		@Override
		public void handleSessionEstablishmentError(Throwable throwable) throws Exception {

		}


        // 修改节点
        zk.writeData("/zkclientdata", "2");

        // 读取节点
        String data = zk.readData("/zkclientdata");
        System.out.println(data);

        // 获取子节点列表
        List<String> childList = zk.getChildren("/zkclientdata");

        // 统计子节点个数
        System.out.println(zk.countChildren("/zkclientdata"));

        // 删除节点
//        zk.delete("/zkclientdata");

        System.in.read();
    }
}

这里只演示基础操作,其它操作请读者自行尝试,API清晰可读。

在zkclient中,节点有三种状态可供订阅,分别用于监控数据的变化、子节点的变化、zk状态的变化:

  • org.I0Itec.zkclient.IZkDataListener
    定义了两种事件,一种是节点数据的变化,另一种是节点被删除。
  • org.I0Itec.zkclient.IZkChildListener
    定义了一种事件,用于监控子节点的变化,这时候获取到的是新的子节点列表。如果此节点被删除,那么获取到的值为null。
  • org.I0Itec.zkclient.IZkStateListener
    定义了三种事件,一种是连接状态的改变,一种是创建一个新的session, 通常是由于session失效后新的session被建立时触发,此时可以恢复数据,比如重新创建临时节点;最后一种是session重建失败。

zkclient强大之处就在于,当发送session过期时能够自动重新订阅这些事件,而不需要开发者重新订阅。


------------本文结束感谢您的阅读------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值