zookeeper学习3之原生java api调用

来源:互联网 发布:js 刷新frame 编辑:程序博客网 时间:2024/06/05 12:47

      建立个maven项目,pom.xml中引入zookeeper的jar。对于zookeeper中创建、删除、修改、查询数据都有同步和异步方式,下面写的例子,都是用同步的方式。如果想看异步、同步、权限控制等例子,可参考http://www.cnblogs.com/leesf456/p/6028416.html

<dependencies>    <dependency>    <groupId>org.apache.zookeeper</groupId>    <artifactId>zookeeper</artifactId>    <version>3.4.9</version>   </dependency>  </dependencies>
在resources文件夹下添加文件

log4j.properties,该文件可以在官网下载zookeeper.3.4.9.tar.gz,解压后,在conf文件夹下

# Define some default values that can be overridden by system propertieszookeeper.root.logger=INFO, CONSOLEzookeeper.console.threshold=INFOzookeeper.log.dir=.zookeeper.log.file=zookeeper.logzookeeper.log.threshold=DEBUGzookeeper.tracelog.dir=.zookeeper.tracelog.file=zookeeper_trace.log## ZooKeeper Logging Configuration## Format is "<default threshold> (, <appender>)+# DEFAULT: console appender onlylog4j.rootLogger=${zookeeper.root.logger}# Example with rolling log file#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE# Example with rolling log file and tracing#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE## Log INFO level and above messages to the console#log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppenderlog4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayoutlog4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n## Add ROLLINGFILE to rootLogger to get log file output#    Log DEBUG level and above messages to a log filelog4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppenderlog4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold}log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}# Max log file size of 10MBlog4j.appender.ROLLINGFILE.MaxFileSize=10MB# uncomment the next line to limit number of backup files#log4j.appender.ROLLINGFILE.MaxBackupIndex=10log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayoutlog4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n## Add TRACEFILE to rootLogger to get log file output#    Log DEBUG level and above messages to a log filelog4j.appender.TRACEFILE=org.apache.log4j.FileAppenderlog4j.appender.TRACEFILE.Threshold=TRACElog4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.file}log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout### Notice we are including log4j's NDC here (%x)log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n

1.1创建zookeeper连接

package com.fei.zk;import java.io.IOException;import java.util.concurrent.CountDownLatch;import java.util.concurrent.TimeUnit;import org.apache.zookeeper.KeeperException;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookeeper.Watcher.Event.KeeperState;import org.apache.zookeeper.ZooKeeper;public class ZkTest01 {private static CountDownLatch latch = new CountDownLatch(1);public static void main(String[] args) throws IOException, KeeperException, InterruptedException {ZooKeeper zk = getZk();while(true){Thread.sleep(5000);}}/** * 创建zk连接 * @return * @throws IOException * @throws InterruptedException */public static ZooKeeper getZk() throws IOException, InterruptedException{//如果是集群,则逗号隔开,比如192.168.0.219:2181,192.168.0.220:2181,192.168.0.221:2181String connStr = "192.168.0.219:2181";int session_time = 5000;//每5秒发送一次心跳ZooKeeper zk = new ZooKeeper(connStr,session_time,new Watcher(){public void process(WatchedEvent event) {if (event.getState() == KeeperState.SyncConnected) {         System.out.println("zk connection OK....");         latch.countDown();//释放阻塞                 } else if (event.getState().equals(KeeperState.Disconnected)) {            // 这时收到断开连接的消息,这里其实无能为力,因为这时已经和ZK断开连接了,只能等ZK再次开启了            System.out.println("zk Disconnected");        } else if (event.getState().equals(KeeperState.Expired)) {                // 这时收到这个信息,表示,ZK已经重新连接上了,但是会话丢失了,这时需要重新建立会话。        System.out.println("zk Expired");                    //这里可以进行zk重新连接操作        //do Some thing....        } else if (event.getState().equals(KeeperState.AuthFailed)) {        System.out.println("zk AuthFailed");        }else {        System.out.println("unkonke...." + event.getType());        }}});System.out.println("zk status=" + zk.getState());latch.await(5000,TimeUnit.MILLISECONDS);//阻塞,等待zk连接成功,或者5s超时,否则不能往下执行System.out.println("zk connection OK,lock release....");return zk;}}
2017-02-22 15:52:44,393 [myid:] - INFO  [main:ZooKeeper@438] - Initiating client connection, connectString=192.168.0.219:2181 sessionTimeout=5000 watcher=com.fei.zk.ZkTest01$1@44efc2d1zk status=CONNECTING2017-02-22 15:52:44,425 [myid:] - INFO  [main-SendThread(192.168.0.219:2181):ClientCnxn$SendThread@1032] - Opening socket connection to server 192.168.0.219/192.168.0.219:2181. Will not attempt to authenticate using SASL (unknown error)2017-02-22 15:52:44,436 [myid:] - INFO  [main-SendThread(192.168.0.219:2181):ClientCnxn$SendThread@876] - Socket connection established to 192.168.0.219/192.168.0.219:2181, initiating session2017-02-22 15:52:44,451 [myid:] - INFO  [main-SendThread(192.168.0.219:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server 192.168.0.219/192.168.0.219:2181, sessionid = 0x15a63f17b3c0001, negotiated timeout = 5000zk connection OK....zk connection OK,lock release....
连接成功了。如果把zk服务器停止掉,让客户端无法连接,会怎样?

2017-02-22 15:55:33,382 [myid:] - INFO  [main-SendThread(192.168.0.219:2181):ClientCnxn$SendThread@1032] - Opening socket connection to server 192.168.0.219/192.168.0.219:2181. Will not attempt to authenticate using SASL (unknown error)2017-02-22 15:55:34,411 [myid:] - WARN  [main-SendThread(192.168.0.219:2181):ClientCnxn$SendThread@1162] - Session 0x0 for server null, unexpected error, closing socket connection and attempting reconnectjava.net.ConnectException: Connection refused: no further informationat sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)at sun.nio.ch.SocketChannelImpl.finishConnect(Unknown Source)at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:361)at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1141)
会不停的输出连接失败的日志,说明当zookeeper连接服务器失败时,它会不停的自动尝试连接。

1.2创建节点

/** * 测试创建节点 * @param zk */public static void createNode(ZooKeeper zk){try {//创建test节点,值为test,不需权限控制,值持久化zk.create("/test", "test".getBytes("UTF-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);System.out.println("/test 创建成功....");} catch (Exception e) {System.out.println("/test 创建失败....");e.printStackTrace();} //尝试创建已存在的节点try {//创建test节点,值为test,不需权限控制,值持久化zk.create("/test", "test".getBytes("UTF-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);System.out.println("第二次 /test 创建成功....");} catch (Exception e) {System.out.println("第二次 /test 创建失败....");e.printStackTrace();} //创建创建父节点不存在,就直接建子节点try {//创建/app/app01节点,值为test,不需权限控制,值持久化zk.create("/app/app01", "app01".getBytes("UTF-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);System.out.println("/app/app01 创建成功....");} catch (Exception e) {System.out.println("/app/app01 /test 创建失败....");e.printStackTrace();} }
zk connection OK....zk connection OK,lock release..../test 创建成功....第二次 /test 创建失败....org.apache.zookeeper.KeeperException$NodeExistsException: KeeperErrorCode = NodeExists for /testat org.apache.zookeeper.KeeperException.create(KeeperException.java:119)at org.apache.zookeeper.KeeperException.create(KeeperException.java:51)at org.apache.zookeeper.ZooKeeper.create(ZooKeeper.java:783)at com.fei.zk.ZkTest01.createNode(ZkTest01.java:90)at com.fei.zk.ZkTest01.main(ZkTest01.java:23)/app/app01 /test 创建失败....org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /app/app01at org.apache.zookeeper.KeeperException.create(KeeperException.java:111)at org.apache.zookeeper.KeeperException.create(KeeperException.java:51)at org.apache.zookeeper.ZooKeeper.create(ZooKeeper.java:783)at com.fei.zk.ZkTest01.createNode(ZkTest01.java:100)at com.fei.zk.ZkTest01.main(ZkTest01.java:23)
可以看到,如果节点已经存在了,再次创建就会抛出异常,如果父节点不存在就直接创建子节点,也会抛出异常.
CreateMode有PERSISTENT, PERSISTENT_SEQUENTIAL, EPHEMERAL, EPHEMERAL_SEQUENTIAL,其中PERSISTENT,PERSISTENT_SEQUENTIAL服务器都会将数据进行持久化;EPHEMERAL, EPHEMERAL_SEQUENTIAL是临时的,在session中有效,也就是如果zk断开了或session过期了,那这些临时节点就不存在了;SEQUENTIAL就是序列,比如如果path写/app-,CreateMode用XXXX_SEQUENTIAL,那最终节点路径就是/app-0000000001(10个数字)

1.3判断节点是否存在

public static void isExist(ZooKeeper zk){String path = "/test";try {Stat stat = zk.exists(path, false);if(stat == null){System.out.println("节点 path="+path+"不存在");}else{System.out.println("节点 path="+path+"存在,stat=" + stat);}} catch (KeeperException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

1.4删除节点

/** * 删除节点 * @param zk */public static void delete(ZooKeeper zk){//删除已存在的节点try {Stat stat = zk.exists("/test", false);zk.delete("/test", stat.getVersion());System.out.println("删除/test成功....");} catch (Exception e) {System.out.println("删除/test节点失败...");e.printStackTrace();}//删除不存在的节点try {zk.delete("/test/test", 0);System.out.println("删除/test/test成功.....");} catch (Exception e) {System.out.println("删除/test/test失败.....");e.printStackTrace();}}
zk connection OK....zk connection OK,lock release....删除/test成功....删除/test/test失败.....org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /test/testat org.apache.zookeeper.KeeperException.create(KeeperException.java:111)at org.apache.zookeeper.KeeperException.create(KeeperException.java:51)at org.apache.zookeeper.ZooKeeper.delete(ZooKeeper.java:873)at com.fei.zk.ZkTest01.delete(ZkTest01.java:146)at com.fei.zk.ZkTest01.main(ZkTest01.java:26)
删除不存在的节点时,会抛出异常。所以删除时最好先判断节点是否存在,同时删除时,节点的版本后也要正确。节点刚创建时,version=0,当对节点进行修改时,version会自增,这是为了避免并发是导致数据不一致.

1.5修改节点数据

public static void update(ZooKeeper zk){try {//创建临时节点/test-update,值为updatezk.create("/test-update", "update".getBytes("utf-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);//把值修改为update-2zk.setData("/test-update", "update-2".getBytes("utf-8"), 0);String data = new String(zk.getData("/test-update", false, null),"utf-8");System.out.println("/test-update的值为"+data);} catch (Exception e) {e.printStackTrace();}

zk connection OK....zk connection OK,lock release..../test-update的值为update-2

1.6 带有权限控制的节点

  如果创建某个节点的客户端zk连接进行了权限认证并且创建节点时使用Ids.CREATOR_ALL_ACL,则其他客户端连接(没认证或认证信息不一致)是无法对这个节点进行读取、修改、删除等操作的。
public static void auth(){try {String connStr = "192.168.0.219:2181";//创建zk1ZooKeeper zk1 = new ZooKeeper(connStr,5000,null);//zk1进行认证zk1.addAuthInfo("digest", "jianfei".getBytes("utf-8"));//zk1创建节点/fei,CREATOR_ALL_ACL进行权限控制zk1.create("/fei", "fei".getBytes("utf-8"), Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);//创建zk2ZooKeeper zk2 = new ZooKeeper(connStr,5000,null);//zk2读取节点System.out.println(new String(zk2.getData("/fei", false, null),"utf-8"));} catch (Exception e) {e.printStackTrace();}}
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /feiat org.apache.zookeeper.KeeperException.create(KeeperException.java:113)at org.apache.zookeeper.KeeperException.create(KeeperException.java:51)at org.apache.zookeeper.ZooKeeper.getData(ZooKeeper.java:1212)at org.apache.zookeeper.ZooKeeper.getData(ZooKeeper.java:1241)at com.fei.zk.ZkTest01.auth(ZkTest01.java:183)at com.fei.zk.ZkTest01.main(ZkTest01.java:28)
看到报NoAuth for /fei的错误.

1.7获取子节点

/** * 获取子节点 * @param zk */public static void getChildrenNode(ZooKeeper zk){try {//创建一些临时节点//注意不能再临时节点下创建子节点zk.create("/app", "app".getBytes("utf-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);//子节点可以是临时的zk.create("/app/app01", "app01".getBytes("utf-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);zk.create("/app/app02", "app02".getBytes("utf-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);List<String> list = zk.getChildren("/app", false);System.out.println("/app的子节点有:");for(String c : list){System.out.println(c);}} catch (Exception e) {e.printStackTrace();}}
zk connection OK....zk connection OK,lock release..../app的子节点有:app02app01

1.8节点监控

    其实上面getZk()方法里,就有对创建zk时连接的监控了。看ZooKeeper的方法,会发现其实在getData,isExist,getChildrenNode等方法上,都有Watch参数。

注意:节点上的监听,都是一次性的,比如getData上监听/test节点,然后修改/test的值,这时服务器会发送通知给客户端,但是第二次修改/test上的值时,服务器是不会再发通知给客户端的,除非客户端获取到第一次通知时,再次进行注册监听。
public static void watch(ZooKeeper zk){try {//监听/watch节点是否存在,Stat stat = zk.exists("/watch", new Watcher(){public void process(WatchedEvent event) {if(event.getType() == EventType.NodeCreated){System.out.println("exists中监听到..."+event.getPath()+"...创建了");}else if(event.getType() == EventType.NodeDeleted){System.out.println("exists中监听到..."+event.getPath()+"...删除了");}else if(event.getType() == EventType.NodeDataChanged){System.out.println("exists中监听到..."+event.getPath()+"...修改了");}}});if(stat == null){System.out.println("/watch 不存在");//创建String s = zk.create("/watch", "watch".getBytes("utf-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);System.out.println("创建/watch成功..." + s);//获取,并监听zk.getData("/watch", new Watcher(){public void process(WatchedEvent event) {if(event.getType() == EventType.NodeCreated){System.out.println("getData中监听到..."+event.getPath()+"...创建了");}else if(event.getType() == EventType.NodeDeleted){System.out.println("getData中监听到..."+event.getPath()+"...删除了");}else if(event.getType() == EventType.NodeDataChanged){System.out.println("getData中监听到..."+event.getPath()+"...修改了");}}}, null);//修改2次zk.setData("/watch", "watch2".getBytes("utf-8"), 0);zk.setData("/watch", "watch3".getBytes("utf-8"), 1);//删除zk.delete("/watch", 2);}else{System.out.println("/watch 已存在");}} catch (Exception e) {e.printStackTrace();}}

/watch 不存在exists中监听到.../watch...创建了创建/watch成功.../watchgetData中监听到.../watch...修改了
看到,就创建和第一次修改的时候,有通知,第二次修改和删除的时候没通知。。。。也许代码这直接写不直观,可以先写exists的监听,然后再服务器上打开./zkCli.sh,然后手动创建节点,修改值等操作,会更加直观对比。
    节点上的监听都是一次性的,如果想一直监听,则需要接收到第一次通知后,再注册一次监听


 1.9原生API的一些方便之处

  通过上面的例子可以看出原生api的一些不方便之处

1)如果父节点不存在,不能直接创建子节点

2)删除时,如果节点不存在,抛出错误

3)节点监控是一次性的,要一直监控,得自己处理

4)连接丢失,需自己处理,比如重连接

  以上是通过上面例子暂时看到的问题





























0 0
原创粉丝点击