zookeeper java客户端使用

ZooKeeper是一个优秀的分布式协同工具,很多分布式项目都基于它进行架构设计,不过要想要对其有一个深入的理解(如果你想阅读其源代码),对其客户端API的熟悉必不可少。下面就简要记录一下ZooKeeper中各个API的简单用法。


这篇文章不打算对ZooKeeper的基本概念及安装进行讲解,想要了解这部分内容可以参考:http://zookeeper.apache.org/doc/r3.4.3/zookeeperOver.html , 
或者可以参考:http://zookeeper.apache.org/doc/r3.4.3/zookeeperProgrammers.html
均是官方文档,这也是想要学习某个开源工具必须的先行步骤,并且官网上的文档也应该算是最权威的,不过ZooKeeper在这方面的文档不怎么多,但作为入门了解,还是非常有用的。


下面将从基本用法,Watchert的用法,异步调用以及ACL四个方面对ZooKeeper客户端编程作简要介绍。
当完成这四个方面的理解以后,就可以使用ZK完成一些更加高级的任务,如分布式锁、Master选举、一致性服务保障、配置管理等。官方文档对此也有简要介绍,
参考:http://zookeeper.apache.org/doc/r3.4.3/recipes.html


基本数据结构

class Stat {  
  private long czxid;  
  private long mzxid;  
  private long ctime;  
  private long mtime;  
  private int version;  
  private int cversion;  
  private int aversion;  
  private long ephemeralOwner;  
  private int dataLength;  
  private int numChildren;  
  private long pzxid;  
}  
class Id {  
  private String scheme;    //world、auth、digest、ip  
  private String id;  
}   
class ACL {  
  private int perms;    //CREATE、READ、WRITE、DELETE、ADMIN  
  private org.apache.zookeeper.data.Id id;  
}



基本使用

try {      
      static String hostport = "127.0.0.1:2181";  
      ZooKeeper zooKeeper = new ZooKeeper(hostport, 300000, null);    //创建一个ZooKeeper实例,不设置默认watcher  
      String path = "/test";  
      zooKeeper.create(path, path.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);    //创建一个节点  
      Stat stat = new Stat();  
      byte[] b = zooKeeper.getData(path, false, stat);    //获取节点的信息及存储的数据  
      System.out.println(stat);  
      System.out.println(new String(b));  
      stat = zooKeeper.exists(path, false);    //查看path所代表的节点是否存在  
      zooKeeper.setData(path, "helloworld".getBytes(), stat.getVersion());    //设置节点的数据  
      //zooKeeper.delete(path, -1);    //删除节点  
      zooKeeper.close();    //关闭实例  
} catch (Exception e) {  
      e.printStackTrace();  
}




ZooKeeper通过Auth和ACL完成节点的权限控制。

Auth表示某种认证,由于一个ZooKeeper集群可能被多个项目使用,各个项目 属于不同的项目组,他们在进行开发时肯定不想其他项目访问与自己相关的节点,这时可以通过为每个项目组分配一个Auth,然后每个项目组先通过Auth认 证以后再继续相关的操作,这样甲Auth认证的用户就不能操作其他Auth认证后创建的节点,从而实现各个项目之间的隔离。ZooKeeper提供了如下 方法完成认证,如下所示:
Void addAuthInfo(String scheme, byte[] auth) ,使用示例如下:

@Test  
    public void testFirstStep() {  
        try {  
            zk = new ZooKeeper(hostport, 1000000, null);  
              
            String auth_type = "digest";  
            String auth = "joey:some";  
              
            String p = "/acl_digest";  
              
            zk.addAuthInfo(auth_type, auth.getBytes());  
              
            zk.create(p, "hello".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);  
              
            Stat stat = new Stat();  
            System.out.println(new String(zk.getData(p, false, stat)));  
  
            zk.close();  
        } catch(Exception ex) {  
            ex.printStackTrace();  
        }  
    }  
    @Test  
    public void testSecondStep() {  
        String p = "/acl_digest";  
      
    try {  
        zk = new ZooKeeper(hostport, 1000000, null);  
          
        String authType = "digest";  
         String badAuth = "joey:someBAD";  
              
            zk.addAuthInfo(authType, badAuth.getBytes());  
            Stat stat = new Stat();  
            System.out.println(new String(zk.getData(p, false, stat)));  
        } catch(Exception ex) {  
            ex.printStackTrace();  //抛出异常  
        } finally {  
            try {  
                zk.delete(p, -1);  
                zk.close();  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
              
        }  
    }


ACL用于控制Znode的访问,和Unix文件访问权限类似,提供对某类用户设置某种权限的能力(如Unix中对Owner提供读、写、执行的权限), 但是在ZooKeeper中没有Owner、Group等概念,于是在ZooKeeper中使用ID表示某一类用户,可以对ID设置某种权限。 (ZooKeeper对ID的数量没有限制,不像Unix文件仅支持三种类型用户)

ZooKeeper支持的权限:
CREATE: you can create a child node
READ: you can get data from a node and list its children.
WRITE: you can set data for a node
DELETE: you can delete a child node
ADMIN: you can set permissions


ZooKeeper内建的sheme:(scheme是ID的其中一个属性)
world has a single id, anyone, that represents anyone.
auth doesn't use any id, represents any authenticated user.
digest uses a username:password string to generate MD5 hash which is then used as an ACL ID identity. Authentication is done by sending theusername:password in clear text. When used in the ACL the expression will be the username:base64 encoded SHA1 password digest.
ip uses the client host IP as an ACL ID identity. The ACL expression is of the form addr/bits where the most significant bits of addr are matched against the most significant bits of the client host IP.


ZK内建的ID:
ANYONE_ID_UNSAFE    //任意用户
AUTH_IDS     //通过Auth认证过的用户


内建的权限控制集合:
OPEN_ACL_UNSAFE: 创建任何人都可以操作的节点
READ_ACL_UNSAFE: 创建任何人都可以读的节点
CREATOR_ALL_ACL: 设置了Auth的用户可以使用该ACL集合创建节点,该节点也只能被同样Auth授权的用户操作


示例代码如下:

@Test  
    public void testACL_with_ip_scheme() {  
        try {  
            Id id = new Id();  
            id.setScheme("ip");  
            id.setId(InetAddress.getLocalHost().getHostAddress());  
              
            ACL acl = new ACL();  
            acl.setId(id);    //对ID所指定的目标设置权限  
            acl.setPerms(Perms.ALL);  
              
            List<ACL> acls = new ArrayList<ACL>();  
            acls.add(acl);    //可以添加多个运行的IP地址  
              
            String p = "/ip";  
              
            zk.create(p, p.getBytes(), acls, CreateMode.PERSISTENT);  
              
            zk.delete(p, -1);  //仅IP相同的用户可以对该进行进行操作  
        } catch(Exception ex) {  
            ex.printStackTrace();  
        }  
    }


Watcher
可以设置Watcher的方式:

1) 在ZooKeeper的构造函数中可以设置Watcher

2) 使用ZooKeeper.register(Watcher)显示的更改在构造函数中设置的默认Watcher

3) 通过某些方法的调用可以更改某个path对应节点的Watcher    

 

具体可以设置Watcher的方法如下所示:

1)     构造函数: state changes or node events 

2)     Register: 修改构造函数中指定的默认Watcher.

3)     getData: triggered by sets data on the node, or deletes the node. 

4)     getChildren: triggered by deletes the node or creates/delete a child under the node. 

5)     exists: triggered by creates/delete the node or sets the data on the node.

 

其中构造函数阶段指定的Watcher一直有效(register方式属于该类),其余方法设置的Watcher仅有效一次。在方法调用时,如果指 定开启watcher,如果该节点通过getData、getChildren和exists设置了Watcher,就触发该Watcher,然后使得该 Watcher失效(但默认的Watcher还一直生效),否则触发构造函数中设定的默认Watcher。

 

示例代码如下:

class ExistsWatcher implements Watcher {   
   @Override  
        public void process(WatchedEvent event) {  
     System.out.println("---------------------------");  
     System.out.println("setting by exist watcher");  
     System.out.println("path is : " + event.getPath());  
     System.out.println("type is : " + event.getType());  
     System.out.println("state is : " + event.getState());  
     System.out.println("---------------------------");  
  }  
 }  
class DefaultWatcher implements Watcher {  
  @Override  
  public void process(WatchedEvent event) {  
    System.out.println("=====>Default Watch Event: " + event.getType());  
  }  
}  
@Test  
  public void testWatcher() {  
    try {  
      DefaultWatcher defaultWatcher = new DefaultWatcher();  
      ExistsWatcher existsWatcher = new ExistsWatcher();  
      String p = "/watcher";  
      ZooKeeper zk = new ZooKeeper(hostport, 300000, null);  
      zk.register(defaultWatcher);  
      Stat stat = zk.exists(p, existsWatcher);  
      zk.create(p, p.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
      stat = zk.exists(p, true);  
      byte[] b = zk.getData(p, true, stat);  
      System.out.println(new String(b));  
      stat = zk.exists(p, true);  
      zk.setData(p, "Iloveyou".getBytes(), stat.getVersion());  
      stat = zk.exists(p, existsWatcher);  
      zk.delete(p, stat.getVersion());  
      zk.close();  
    } catch(Exception ex) {  
      ex.printStackTrace();  
    }  
  }



运行结果如下:

=====>Default Watch Event: None  
---------------------------                        
setting by exist watcher  
path is : /watcher  
type is : NodeCreated  
state is : SyncConnected  
---------------------------  
/watcher  
=====>Default Watch Event: NodeDataChanged  
---------------------------  
setting by exist watcher  
path is : /watcher  
type is : NodeDeleted  
state is : SyncConnected  
---------------------------


异步调用
顾名思义,异步调用是指在调用某个方法后不等待其返回,而是接着处理下面的任务,当方法调用完成时触发某个回调函数,回调函数需要在方法调用时指定,然后在回调函数中处理方法调用的结果。
在ZK中,几乎为每个方法都提供了异步调用的版本,如getData方法,其函数原型如下所示:


void getData(String path, boolean watch, DataCallback cb, Object ctx);
其中: 
DataCallback为提供回调函数的类,
ctx为回调函数需要的参数


示例代码如下:

Children2Callback cb = new Children2Callback() {  
            @Override  
            public void processResult(int rc, String path, Object ctx,  
                    List<String> children, Stat stat) {  
                for (String s : children) {  
                    System.out.println("----->" + s);  
                }  
                System.out.println(ctx);    //输出:helloworld  
            }  
        };  
          
        zk.getChildren(path, true, cb, "helloworld");

你可能感兴趣的:(zookeeper java客户端使用)