1 Zookeeper安装以及启动
这里我已经进行了安装,并且启动了Zookeeper。端口是2182
2 Zookeeper config
tickTime=2000
initLimit=10
syncLimit=5
dataDir=D://zookiper/zookeeper/data
clientPort=2182
参数介绍
tickTime: 这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
dataDir: 顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
initLimit: 这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集 群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4 秒
3 连接zk服务器
依赖的pom
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.3-beta</version>
<type>pom</type>
</dependency>
public static ZooKeeper zkClient(){
final String connectString="127.0.0.1:2182";
final int sessionTimeout=5000;
ZooKeeper zooKeeper=null;
try {
zooKeeper=new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(final WatchedEvent event) {
/** 判断是否和服务器之间取得了连接*/
if(event.getState()==Event.KeeperState.SyncConnected){
System.out.println("已经触发了" + event.getType() + "事件!");
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
return zooKeeper;
}
在上述中,connectString,sessionTimeout表示连接服务器的地址,以及客户端连接服务器端的session超时时间。watcher为监视器。zookeeper api和服务器之间的连接是异步的。当执行 ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, watcher) 这句代码之后,会立马返回zkclient;当与服务器建立好连接之后会 ,调用Watcher中的process方法进行处理。 process方法会接受一个WatchedEvent类型的参数,用于表明发生了什么事件。
下属代码块可以作为连接zk的模板。
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { //判断是否已连接
if(watchedEvent.getType() == Event.EventType.None && null == watchedEvent.getPath()) {
// 最初与zk服务器建立好连接
} else if(watchedEvent.getType() == Event.EventType.NodeChildrenChanged) {
// 子节点变化事件
}
// ...还可以继续监听其它事件类型
}
System.out.println(watchedEvent.getState());
}
WatchedEvent包含两方面重要信息:
1 与zk服务器连接的状态信息
可以调用watchedEvent.getState()
方法获取与zk服务器连接的状态信息,状态信息取值主要包括SyncConnected、Disconnected、ConnectedReadOnly和AuthFailed等等。
2 发生的具体事件类型信息watchedEvent.getState()
方法只是获取与zk服务器连接的状态信息,但在同一个连接状态下,还会发生很多事件的类型。例如在zk中,我们可以watch一个节点的数据内容,当这个节点的数据被改变时,我们可以获取到这个事件。类似的还有子节点列表变化事件等等。这就需要我们在SyncConnected同一种连接状态下区分多个事件类型。可以通过watchedEvent.getType()
方法获取具体的事件类型。
事件类型的取值包括None、NodeCreated、NodeDeleted、NodeDataChanged和NodeChildrenChanged。
4 创建节点
下面要介绍的每种api操作都可以分为两种类型——同步和异步。同步操作一般会有返回值,并且会抛出相应的异常。异步操作没有返回值,也不会抛出异常。此外异步方法参数在同步方法参数的基础上,会增加Callback和context两个参数。如用同步方式创建一个节点的的代码如下:
- 创建同步节点
public static void createNodeSync() throws KeeperException, InterruptedException {
String path = "/poype_node";
ZooKeeper zooKeeper = zkClient();
String nodePath = zooKeeper.create(path, "123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(nodePath);
}
看ZkClient创建节点完毕:
关于上述的GUI工具,可以参见这篇文章:https://www.cnblogs.com/easyworld/p/8463910.html
同步下创建节点的方法create(*)介绍
public String create(final String path, byte data[],
List<ACL> acl,CreateMode createMode)
throws KeeperException, InterruptedException
参数介绍:
- path:创建节点的路径
- data[] : 创建节点的数据值,参数类型是字节数组
- acl:节点的访问权限,我们这里指定该节点可以被任何人访问
createMode:create命令可以有-s和-e两个参数,其中-s是顺序节点,-e是临时节点。这里的CreateMode就是这两个参数的组合。可选的值:
PERSISTENT |
永久节点 |
PERSISTENT_SEQUENTIAL |
永久有序节点 |
EPHEMERAL |
临时节点 |
EPHEMERAL_SEQUENTIAL |
临时有序节点 |
- 创建异步节点
异步模式方法没有返回值,并且不会抛出任何异常:除了同步create方法中的四个参数以外,异步模式的create方法还增加了callback和context两个参数。StringCallback接口中的processResult方法会在节点创建好之后被调用,它有四个参数。第一个是int类型的resultCode,作为创建节点的结果码,当成功创建节点时,resultCode的值为0。第二个参数是创建节点的路径。第三个参数是context,当一个StringCallback类型对象作为多个create方法的参数时,这个参数就很有用了。第四个参数是创建节点的名字,其实与path参数相同。
在我用其中的一种方式,怎么也创建不了节点,就是如下的代码:
public static ZooKeeper zkClient(){
/** connectString ZK连接地址
* sessionTimeout 回话超时时间 单位ms
* ************************************************
* watcher 监视器
* 关于监视器:zookeeper api与服务器建立连接的过程是异步的。
* ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, watcher);
* 上面的调用会马上从ZooKeeper构造函数返回,当与服务器建立好连接之后会
* 调用Watcher中的process方法进行处理。
* process方法会接受一个WatchedEvent类型的参数,用于表明发生了什么事件。*/
final String connectString="127.0.0.1:2182";
final int sessionTimeout=5000;
ZooKeeper zooKeeper=null;
try {
zooKeeper=new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(final WatchedEvent watchedEvent) {
/** 判断是否和服务器之间取得了连接*/
if(watchedEvent.getState()==Event.KeeperState.SyncConnected){
System.out.println("已经触发了" + watchedEvent.getType() + "事件!");
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
return zooKeeper;
}
private static void createNodeAsync() {
String path = "/poype_node2";
ZooKeeper zooKeeper = zkClient();
zooKeeper.create(path, "123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,
new MyStringCallBack(), "create");
}
static class MyStringCallBack implements AsyncCallback.StringCallback{
@Override
public void processResult(final int rc, final String path, final Object ctx, final String name) {
System.out.println("异步创建回调结果:状态:" + rc +";创建路径:" +
path + ";传递信息:" + ctx + ";实际节点名称:" + name);
}
}
最后采用另外一种方式创建了节点:
- 同步方式获取节点数据
public class CreateNode implements Watcher {
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2182", 5000, new CreateNode());
countDownLatch.await();
/** 异步创建临时节点*/
zooKeeper.create("/poype_node2", "abc".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new MyStringCallBack(), "我是传递内容");
/** 验证等待回调结果使用,可根据实际情况自行调整*/
Thread.sleep(10000);
}
@Override
public void process(WatchedEvent event) {
if (Event.KeeperState.SyncConnected == event.getState()) {
countDownLatch.countDown();
}
}
static class MyStringCallBack implements AsyncCallback.StringCallback {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
System.out.println("异步创建回调结果:状态:" + rc +";创建路径:" +
path + ";传递信息:" + ctx + ";实际节点名称:" + name);
}
}
}
zooKeeper.getData方法的返回值就是节点中存储的数据值,它有三个参数,第一个参数是节点的路径,用于表示要获取哪个节点中的数据。第三个参数stat用于存储节点的状态信息,在调用getData方法前,会先构造一个空的Stat类型对象作为参数传给getData方法,当getData方法调用返回后,节点的状态信息会被填充到stat对象中。
第二个参数是一个bool类型的watch,这个参数比较重要。当watch为true时,表示我们想要监控这个节点的数据变化。当节点的数据发生变化时,我们就可以拿到zk服务器推送给我们的通知。在process方法中会有类似下面的代码:
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { //与zk服务器处于连接状态
if(watchedEvent.getType() == Event.EventType.None && null == watchedEvent.getPath()) {
createNodeAsync();
} else if(watchedEvent.getType() == Event.EventType.NodeChildrenChanged) {
// 节点的子节点列表发生变化
} else if(watchedEvent.getType() == Event.EventType.NodeDataChanged) {
// 节点的数据内容发生变化
}
}
}
当节点的数据内容发生变化时,我们就会接收到NodeDataChanged这个事件。值得注意的是,zooKeeper设置的监听只生效一次,如果在接收到NodeDataChanged事件后还想继续对该节点的数据内容改变进行监听,需要在事件处理逻辑中重新调用getData方法并将watch参数设置为true。
异步获取一个节点数据值的代码如下:
- 异步方式获取节点数据
ublic class CreateNode implements Watcher {
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2182", 5000, new CreateNode());
countDownLatch.await();
zooKeeper.getData("/poype_node2",true,new MyStringCallBackData(),"异步获取节点的数据值");
Thread.sleep(10000);
}
@Override
public void process(WatchedEvent event) {
if (Event.KeeperState.SyncConnected == event.getState()) {
countDownLatch.countDown();
}
}
static class MyStringCallBackData implements AsyncCallback.DataCallback{
@Override
public void processResult(final int rc, final String path, final Object ctx,
final byte[] data, final Stat stat) {
System.out.println(rc);
System.out.println(path);
System.out.println(ctx);
System.out.println("***********");
System.out.println(new String(data));
System.out.println(stat);
}
}
}
参考:https://segmentfault.com/a/1190000012262940
: https://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/index.html