上一章我们知道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

zookeeper学习系列:二、api实践的更多相关文章

  1. ZooKeeper学习笔记二:API基本使用

    Grey ZooKeeper学习笔记二:API基本使用 准备工作 搭建一个zk集群,参考ZooKeeper学习笔记一:集群搭建. 确保项目可以访问集群的每个节点 新建一个基于jdk1.8的maven项 ...

  2. [转]ASP.NET MVC学习系列(二)-WebAPI请求 传参

    [转]ASP.NET MVC学习系列(二)-WebAPI请求 传参 本文转自:http://www.cnblogs.com/babycool/p/3922738.html ASP.NET MVC学习系 ...

  3. RabbitMQ学习系列二-C#代码发送消息

    RabbitMQ学习系列二:.net 环境下 C#代码使用 RabbitMQ 消息队列 http://www.80iter.com/blog/1437455520862503 上一篇已经讲了Rabbi ...

  4. 图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)

    项目链接:https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType=1 欢迎fork欢迎三连!文章篇幅有限, ...

  5. MyBatis学习系列二——增删改查

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring 数据库的经典操作:增删改查. 在这一章我们主要说明一下简单的查询和增删改, ...

  6. Maven学习系列二(1-5)

    Maven学习系列二(1-5) 本文转自 QuantSeven 博客,讲解精炼易懂,适合入门,链接及截图如下 http://www.cnblogs.com/quanyongan/category/47 ...

  7. scrapy爬虫学习系列二:scrapy简单爬虫样例学习

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  8. DocX开源WORD操作组件的学习系列二

    DocX学习系列 DocX开源WORD操作组件的学习系列一 : http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_sharp_001_docx1.htm ...

  9. .net reactor 学习系列(二)---.net reactor界面各功能说明

    原文:.net reactor 学习系列(二)---.net reactor界面各功能说明         安装了.net reactor之后,可以在安装目录下找到帮助文档REACTOR_HELP.c ...

  10. ASP.NET MVC学习系列(二)-WebAPI请求

    继续接着上文 ASP.NET MVC学习系列(一)-WebAPI初探 来看看对于一般前台页面发起的get和post请求,我们在Web API中要如何来处理. 这里我使用Jquery 来发起异步请求实现 ...

随机推荐

  1. Markdown 语法和 MWeb 写作使用说明

    ---恢复内容开始--- # Markdown 语法和 MWeb 写作使用说明 Markdown 的设计哲学 Markdown 的目標是實現「易讀易寫」. 不過最需要強調的便是它的可讀性.一份使用 M ...

  2. 前端里神奇的BFC 原理剖析

    以前在做自适应两栏布局的时候别人口中听到bfc这个词,于是看了各种关于bfc的文章,发现梦想天空介绍的不错,今天就在他的基础上润色一下. 一.BFC是什么? 在解释 BFC 是什么之前,需要先介绍 B ...

  3. array_merge注意细节

    array_merge:合并一个或多个数组,一个数组中的值加在前一个数组的后面,返回的新数组作为结果 如果输入的数组中有相同的字符串键名,则该键名后面的值覆盖前面的,如果数组包含相同的数字键名,后面的 ...

  4. IIS7的集成模式下如何让自定义的HttpModule不处理静态文件(.html .css .js .jpeg等)请求

    今天将开发好的ASP.NET站点部署到客户的服务器上后,发现了一个非常头疼的问题,那么就是IIS7的应用程序池是集成模式的话,ASP.NET项目中自定义的HttpModule会处理静态文件(.html ...

  5. static const vs. extern const

    在实现文件(.m文件)中使用static const来定义“只在编译单元内可见的常量”(只在.m文件内可见),由于此类常量不在全局符号表中,所以无须为其名称加类名前缀(一般以k开头). 在头文件中使用 ...

  6. fgets函数

    打开文件 fopen("需要打开的路径") 然后使用fgets函数读取行 #include <stdio.h> #include <stdlib.h> #i ...

  7. Linux之常用快捷键

    tab:自动补齐命令或者路径 ESC+u:将字符小写变大写 ctrl+s:在终端中冻结stdin ctrl+q:在终端中恢复stdin ctrl+a:光标移动到行首 ctrl+e:光标移动到行尾 ct ...

  8. asp.net 之 购物车

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  9. HTML 事件属性

    HTML 事件属性一:定义二:窗口事件 (Window Events)三:表单元素事件 (Form Element Events)四:图像事件 (Image Events) 一:定义 HTML 4 的 ...

  10. 【Spring】Spring的定时任务

    > 参考的优秀文章 Task Execution and Scheduling > 版本说明 <dependencies> <dependency> <gro ...