zookeeper学习3之原生java api调用

      建立个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 properties
zookeeper.root.logger=INFO, CONSOLE
zookeeper.console.threshold=INFO
zookeeper.log.dir=.
zookeeper.log.file=zookeeper.log
zookeeper.log.threshold=DEBUG
zookeeper.tracelog.dir=.
zookeeper.tracelog.file=zookeeper_trace.log

#
# ZooKeeper Logging Configuration
#

# Format is "<default threshold> (, <appender>)+

# DEFAULT: console appender only
log4j.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.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.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 file
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold}
log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}

# Max log file size of 10MB
log4j.appender.ROLLINGFILE.MaxFileSize=10MB
# uncomment the next line to limit number of backup files
#log4j.appender.ROLLINGFILE.MaxBackupIndex=10

log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.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 file
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.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:2181
				String 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@44efc2d1
zk status=CONNECTING
2017-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 session
2017-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 = 5000
zk 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 reconnect
java.net.ConnectException: Connection refused: no further information
	at 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 /test
	at 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/app01
	at 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 block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.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/test
	at 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,值为update
			zk.create("/test-update", "update".getBytes("utf-8"), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
			//把值修改为update-2
			zk.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";
			//创建zk1
			ZooKeeper 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);
			
			
			//创建zk2
			ZooKeeper 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 /fei
	at 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的子节点有:
app02
app01

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


 1.9原生API的一些方便之处

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

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

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

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

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

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





























  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值