设为首页 收藏本站
查看: 987|回复: 0

[经验分享] zookeeper学习系列:二、api实践

[复制链接]

尚未签到

发表于 2015-9-6 10:06:41 | 显示全部楼层 |阅读模式
  上一章我们知道zookeeper的简介,启动,设置节点以及结构性能。本小节我们来玩玩api,获取下数据。
  php版本: http://anykoro.sinaapp.com/2013/04/05/%E4%BD%BF%E7%94%A8apache-zookeeper%E5%88%86%E5%B8%83%E5%BC%8F%E9%83%A8%E7%BD%B2php%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F/
  go版本:http://mmcgrana.github.io/2014/05/getting-started-with-zookeeper-and-go.html
  读一下:http://zookeeper.apache.org/doc/trunk/javaExample.html  
  然后我说 what the fuck it is?
  我就想读个数据,需要这么复杂么。。。
  动手改一下
  版本1:  只获取数据,不管别的:



import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
public class ZkReader {
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
String hostPort = "192.168.1.2,192.168.1.3,192.168.1.4";
String znode = "/test";
ZooKeeper zk = new ZooKeeper(hostPort, 3000, null);
System.out.println(new String(zk.getData(znode,false,null)));
}
}
  在zkcli上创建 /test 并改变它的值:123,运行,输出:
  123
  能得到结果,但是报错了:



14/10/17 11:51:58 ERROR zookeeper.ClientCnxn: Error while calling watcher
java.lang.NullPointerException
at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:521)
at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:497)
  看下源码,需要注册个watcher,意思是不这样zookeeper就只是个纯配置了?ok
  版本2:zk get data+watcher



import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
public class ZkReader {
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
String hostPort = "10.16.73.22,10.16.73.12,10.16.73.13";
String znode = "/test";
ZooKeeper zk = new ZooKeeper(hostPort, 3000, new MyWatcher());
System.out.println(new String(zk.getData(znode,false,null)));
}
}
class MyWatcher  implements Watcher {
@Override
public void process(WatchedEvent event) {
System.out.println("hello zookeeper");
System.out.println(String.format("hello event! type=%s, stat=%s, path=%s",event.getType(),event.getState(),event.getPath()));
}
}
  输出却是:
  hello zookeeper
123
hello event! type=None, stat=SyncConnected, path=null
  data总是在中间?百撕不得姐,在邮件组里咨询下,几天后有了回复(不够活跃的邮件组了):
  “
Zookeeper works asynchronously in several threads. Therefore the sequence of execution in different threads is not generally predictable. It could therefore happen that when the connection status change is detected, the Watcher is executed, but only the first "hello zookeeper" gets echoed, then the main thread gets some cycles again and prints "123", after which the second print statement "hello event!..." is executed. If you don't want this to happen, use a CountDownLatch to make the main thread wait until the Zookeeper connection is established and propertly recognized in your program. The main thread creates the CountDownLatch(1), opens the Zk connection and waits latch.await(). The Watcher does its job and then counts the latch down by one, causing the main thread to leave the await and continue doing its job.
  ”
  被认为是多线程问题,建立zk连接时会启动多个线程:sendThread  eventThread
  eventThread执行到一半时,主线程获得了cpu,打印出结果,然后eventThread继续执行watcher.process。
  
  这两个版本只是做到了获取数据,如果数据有变动,需要自动更新呢?ok,参照zk给的例子,简化出第三个版本:
  DataMonitor.java :



/**
* A simple class that monitors the data and existence of a ZooKeeper
* node. It uses asynchronous ZooKeeper APIs.
*/
import java.util.Arrays;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.AsyncCallback.StatCallback;
import org.apache.zookeeper.data.Stat;
public class DataMonitor implements Watcher, StatCallback {
ZooKeeper zk;
String znode;
boolean dead;
DataMonitorListener listener;
byte prevData[];
public DataMonitor(ZooKeeper zk, String znode,  DataMonitorListener listener) {
this.zk = zk;
this.znode = znode;
this.listener = listener;
// Get things started by checking if the node exists. We are going
// to be completely event driven
zk.exists(znode, true, this, null);
}
/**
* Other classes use the DataMonitor by implementing this method
*/
public interface DataMonitorListener {
/**
* The existence status of the node has changed.
*/
void showData(byte data[]);
}
public void process(WatchedEvent event) {
String path = event.getPath();
if (event.getType() != Event.EventType.None) {
System.out.println("watch event type: "+event.getType());
if (path != null && path.equals(znode)) {
// Something has changed on the node, let's find out
zk.exists(znode, true, this, null);
}
}
}
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println("rc : "+rc);
byte b[] = null;
try {
b = zk.getData(znode, false, null);
} catch (KeeperException e) {
// We don't need to worry about recovering now. The watch
// callbacks will kick off any exception handling
                e.printStackTrace();
} catch (InterruptedException e) {
return;
}
if ((b == null && b != prevData)
|| (b != null && !Arrays.equals(prevData, b))) {
listener.showData(b);
prevData = b;
}
}
}
  Executor.java:



import java.io.IOException;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class Executor
implements Watcher, Runnable, DataMonitor.DataMonitorListener
{
DataMonitor dm;
ZooKeeper zk;
public Executor(String hostPort, String znode) throws KeeperException, IOException {
zk = new ZooKeeper(hostPort, 3000, this);
dm = new DataMonitor(zk, znode, this);
}
public static void main(String[] args) {
String hostPort = "192.168.1.22,192.168.1.12,192.168.1.13";
String znode = "/test";
try {
new Executor(hostPort, znode).run();
} catch (Exception e) {
e.printStackTrace();
}
}
/***************************************************************************
* We do process any events ourselves, we just need to forward them on.
*
* @see org.apache.zookeeper.Watcher#
*/
public void process(WatchedEvent event) {
System.out.println("Executor process event: "+event.getType());
dm.process(event);
}
public void run() {
try {
synchronized (this) {
while (true) {
wait();
}
}
} catch (InterruptedException e) {
}
}
public void showData(byte[] data) {
System.out.println("data changes: "+new String(data));
}
}
  一个执行者一个监控,注册watcher到zk,当有事件发生时,推送本身的StatCallback到Zookeeper,当节点有变动时调用processResult展示结果。
  Executor process event: NodeDataChanged
watch event type: NodeDataChanged
rc : 0
data changes: abcd
  
  还是有点复杂,仔细看下DataMonitor似乎没有存在的必要,我只需要一个类,启动zk client,并监听数据变化就好了,于是有了第四个单对象版本:

Executor.java


import java.io.IOException;
import java.util.Arrays;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
public class Executor
implements Watcher, Runnable, AsyncCallback.StatCallback
{
ZooKeeper zk;
String znode;
byte prevData[];
public Executor(String hostPort, String znode) throws KeeperException, IOException {
zk = new ZooKeeper(hostPort, 3000, this);
this.znode = znode;
zk.exists(znode, true, this, null);
}
public static void main(String[] args) {
String hostPort = "10.16.73.22,10.16.73.12,10.16.73.13";
String znode = "/test";
try {
new Executor(hostPort, znode).run();
} catch (Exception e) {
e.printStackTrace();
}
}
/***************************************************************************
* We do process any events ourselves, we just need to forward them on.
*
* @see org.apache.zookeeper.Watcher#
*/
public void process(WatchedEvent event) {
String path = event.getPath();
if (event.getType() != Event.EventType.None) {
System.out.println("watch event type: "+event.getType());
if (path != null && path.equals(znode)) {
// Something has changed on the node, let's find out
zk.exists(znode, true, this, null);
}
}
}
public void run() {
try {
synchronized (this) {
while (true) {
wait();
}
}
} catch (InterruptedException e) {
}
}
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println("rc : "+rc);
byte b[] = null;
try {
b = zk.getData(znode, false, null);
} catch (KeeperException e) {
// We don't need to worry about recovering now. The watch
// callbacks will kick off any exception handling
            e.printStackTrace();
} catch (InterruptedException e) {
return;
}
if ((b == null && b != prevData)
|| (b != null && !Arrays.equals(prevData, b))) {
System.out.println("data changes: "+new String(b));
prevData = b;
}
}
}
  自己做watcher,并注册回调函数给zk,更简洁。
  经测试,zk三台停掉一台,剩一主一从,仍能正常服务,剩一台时则报错,无法连接,重启动zk变成两台,客户端也无法恢复,重启了才恢复。
  看了php api,理解了一下zk.exists 做的操作,exists和get方法都会注册回调过去,一个是注册watcher,一个是注册StatCallback,当触发事件时,监视器会被消费掉,所以我们需要在回调函数中再次设置监视器。于是有了第五个版本



import java.io.IOException;
import org.apache.zookeeper.*;
public class Executor
implements Watcher, Runnable
{
ZooKeeper zk;
String znode;
public Executor(String hostPort, String znode) throws KeeperException, IOException, InterruptedException {
zk = new ZooKeeper(hostPort, 30000, this);
this.znode = znode;
zk.getData(znode, this, null);
}
public static void main(String[] args) {
String hostPort = "10.16.73.22,10.16.73.12,10.16.73.13";
String znode = "/test";
try {
new Executor(hostPort, znode).run();
} catch (Exception e) {
e.printStackTrace();
}
}
public void process(WatchedEvent event) {
String path = event.getPath();
if (event.getType() != Event.EventType.None) {
System.out.println("watch event type: "+event.getType());
if (path != null && path.equals(znode)) {
// Something has changed on the node, let's find out
try {
System.out.println(new String(zk.getData(znode, this, null)));
} catch (KeeperException e) {
e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
} catch (InterruptedException e) {
e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                }
}
}
}
public void run() {
try {
synchronized (this) {
while (true) {
wait();
}
}
} catch (InterruptedException e) {
}
}
}
  
  上边这两个版本已经可以检测到zk的数据节点变动,但没有处理异常情况,没有处理close事件,大家可以自己动手改造下难懂的http://zookeeper.apache.org/doc/trunk/javaExample.html  例子。
  更多java api操作(创建节点、删除修改等):http://www.cnblogs.com/haippy/archive/2012/07/19/2600032.html
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-110016-1-1.html 上篇帖子: ZooKeeper Distributed模式 下篇帖子: ZooKeeper权限控制
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表