部署了taokeeper,发现作者的博客中介绍了java API的内容,故搬过来学习!
zooKeeper Java API 使用样例
zookeeper系列文章
CountDownLatch应用
官网zookeeper API
1、zookeeper操作
import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* ZooKeeper Java Api 使用样例<br>
* ZK Api Version: 3.4.5
*/
public class JavaAPISample implements Watcher{
private static final int SESSION_TIMEOUT = 10000;
private static final String CONNECTION_STRING = "flink:2181,data0:2181,mf:2181";
private static final String ZK_PATH = "/lmalds";
private ZooKeeper zk = null;
private CountDownLatch connectedSemaphore = new CountDownLatch( 1 );
/**
* 创建ZK连接
* @param connectString ZK服务器地址列表
* @param sessionTimeout Session超时时间
*/
public void createConnection( String connectString, int sessionTimeout ) {
this.releaseConnection();
try{
zk = new ZooKeeper(connectString, sessionTimeout, this);
connectedSemaphore.await();
}catch (InterruptedException e){
System.out.println("连接创建失败,发生InterruptedException");
e.printStackTrace();
}catch (IOException e){
System.out.println("连接创建失败,发生IOException");
e.printStackTrace();
}
}
/**
* 关闭ZK连接
*/
public void releaseConnection() {
if( zk != null){
try{
this.zk.close();
}catch( InterruptedException e ){
// ignore
e.printStackTrace();
}
}
}
/**
* 创建节点
* @param path 节点path
* @param data 初始数据内容
* @return
*/
public boolean creatPath( String path, String data ) {
try{
System.out.println("节点创建成功,Path: " + this.zk.create( path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL ) + ", Content: " + data);
}catch ( KeeperException e){
System.out.println("节点创建失败,发生KeeperException");
e.printStackTrace();
}catch ( InterruptedException e ){
System.out.println("节点创建失败,发生InterruptedException");
e.printStackTrace();
}
return true;
}
/**
* 读取指定节点数据内容
* @param path 节点path
* @return
*/
public String readData( String path ) {
try{
System.out.println( "获取数据成功,path:" + path);
return new String ( this.zk.getData( path,this,null ) );
}catch ( KeeperException e ){
System.out.println( "节点读取失败,发生KeeperException" );
e.printStackTrace();
return "";
}catch ( InterruptedException e ){
System.out.println( "节点读取失败,发生InterruptedException" );
e.printStackTrace();
return "";
}
}
/**
* 更新指定节点数据内容
* @param path 节点path
* @param data 数据内容
* @return
*/
public boolean writeData( String path, String data ) {
try{
System.out.println("数据更新成功, path :" + path + ",Stat :" + this.zk.setData( path, data.getBytes(),-1 ) );
}catch ( KeeperException e ) {
System.out.println( "数据更新失败,发生KeeperException" );
e.printStackTrace();
}catch ( InterruptedException e ) {
System.out.println( "数据更新失败,发生InterruptedException" );
e.printStackTrace();
}
return false;
}
/**
* 删除指定节点
* @param path 节点path
*/
public void deleteNode( String path ) {
try{
this.zk.delete( path, -1 );
System.out.println( "删除节点成功,path:" + path );
}catch ( KeeperException e ) {
System.out.println( "节点删除异常,发生KeeperException, path :" + path );
e.printStackTrace();
}catch ( InterruptedException e ) {
System.out.println( "节点删除异常,发生InterruptedException, path :" + path );
e.printStackTrace();
}
}
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println( "收到事件通知:" + watchedEvent.getState() +"\n" );
if( Event.KeeperState.SyncConnected == watchedEvent.getState() ) {
connectedSemaphore.countDown();
}
}
}
main函数:
public static void main( String args[] ) {
JavaAPISample sample = new JavaAPISample();
sample.createConnection( CONNECTION_STRING, SESSION_TIMEOUT );
if( sample.creatPath( ZK_PATH, "我是节点初始内容" ) ){
System.out.println();
System.out.println("数据内容:" + sample.readData( ZK_PATH ) + "\n" );
sample.writeData( ZK_PATH, "我是更新后的数据" );
System.out.println( "数据内容: " + sample.readData( ZK_PATH ) + "\n" );
sample.deleteNode( ZK_PATH );
}
sample.releaseConnection();
}
输出结果:
收到事件通知:SyncConnected
节点创建成功,Path: /lmalds, Content: 我是节点初始内容
获取数据成功,path:/lmalds
数据内容:我是节点初始内容
收到事件通知:SyncConnected
数据更新成功, path :/lmalds,Stat :64424512593,64424512594,1480992688627,1480992688641,1,0,0,241133777235476532,24,0,64424512593
获取数据成功,path:/lmalds
数据内容: 我是更新后的数据
收到事件通知:SyncConnected
删除节点成功,path:/lmalds
2、疑问
关于输出结果,对于znode的每一次更新,删除操作,都会触发一次watch。主要原因是readData方法, 我的方法如下:
public String readData( String path ) {
try{
System.out.println( "获取数据成功,path:" + path);
return new String ( this.zk.getData( path,this,null ) );
}catch ( KeeperException e ){
System.out.println( "节点读取失败,发生KeeperException" );
e.printStackTrace();
return "";
}catch ( InterruptedException e ){
System.out.println( "节点读取失败,发生InterruptedException" );
e.printStackTrace();
return "";
}
}
注意this.zk.getData( path,this,null )
,第二个参数是this,即指定了watch方法,所以之后对此znode进行的set、delete操作,都会触发这个watch。官方文档中有下面这段解释:
getData
public byte[] getData(String path,
Watcher watcher,
Stat stat)
throws KeeperException,
InterruptedException
Return the data and the stat of the node of the given path.
If the watch is non-null and the call is successful (no exception is thrown), a watch will be left on the node with the given path. The watch will be triggered by a successful operation that sets data on the node, or deletes the node.
如果指定的watch不为空,那么这个watch将留在此znode,之后对此znode的set、delete操作,都会触发这个watch的操作。
而setData也有提到:
setData
public Stat setData(String path,
byte[] data,
int version)
throws KeeperException,
InterruptedException
Set the data for the node of the given path if such a node exists and the given version matches the version of the node (if the given version is -1, it matches any node's versions). Return the stat of the node.
This operation, if successful, will trigger all the watches on the node of the given path left by getData calls.
set操作将会触发watch,此watch为getData时留下的watch。
3、getData时不指定watch
我改一下程序,在getData时将watch设为false:
public String readData( String path ) {
try{
System.out.println( "获取数据成功,path:" + path);
return new String ( this.zk.getData( path,false,null ) );
}catch ( KeeperException e ){
System.out.println( "节点读取失败,发生KeeperException" );
e.printStackTrace();
return "";
}catch ( InterruptedException e ){
System.out.println( "节点读取失败,发生InterruptedException" );
e.printStackTrace();
return "";
}
}
此时,输出结果如下:
收到事件通知:SyncConnected
节点创建成功,Path: /lmalds, Content: 我是节点初始内容
获取数据成功,path:/lmalds
数据内容:我是节点初始内容
数据更新成功, path :/lmalds,Stat :64424512599,64424512600,1480994702615,1480994702629,1,0,0,169111488337543171,24,0,64424512599
获取数据成功,path:/lmalds
数据内容: 我是更新后的数据
删除节点成功,path:/lmalds
我们看到,之后对znode进行的set、delete操作,没有触发watch。
4、总结
zookeeper java api,create时覆写watch方法,在getData时注意参数watch的引入,如果为空,则之后set、delete操作将不会触发watch;而如果引入watch,则set、delete则会触发watch。
关于CountDownLatch,主要用于一个任务,需要等到其他任务(子线程)结束时,才会执行。场景如主线程等待子线程完成才继续执行等操作。