操作

create:创建一个znode(必须要有父节点)
delete:删除一个znode(该znode不能有任何子节点)
exists:测试一个znode是否存在并且查询它的元数据
getACL,setACL:获取/设置一个znode的ACL
getChildren:获取一个znode的子节点列表
getData,setData:获取/设置一个znode所保存的数据
sync:将客户端的znode视图与ZooKeeper同步

ZooKeeper中的更新操作是有条件的。在使用delete或setData操作时必须提供被更新znode的版本号(可以通过exists操作获得)。如果版本号不匹配,则更新操作会失败。更新操作是非阻塞操作,因此一个更新失败的客户端(由于其他进程同时在更新同一个znode)可以决定是否重试,或执行其他操作,并不会因此而阻塞其他进程的执行。
虽然ZooKeeper可以被看作是一个文件系统,但出于简单性的需要,有一些文件系统的基本操作被它摒弃了。由于ZooKeeper中的文件较小并且总是被整体读写,因此没有必要提供打开、关闭或查找操作。
Sync操作与POSIX文件系统中的fsync()操作是不同的。如前所述,ZooKeeper中的写操作具有原子性,一个成功的写操作会保证将数据写到ZooKeeper服务器的持久存储介质中。然而,ZooKeeper允许客户端读到的数据滞后于ZooKeeper服务的最新状态,因此客户端可以使用sync操作来获取数据的最新状态。

1. 集合更新(Multiupdate)

ZooKeeper中有一个被称为multi的操作,用于将多个基本操作集合成一个操作单元,并确保这些基本操作同时被成功执行,或者同时失败,不会发生其中部分基本操作被成功执行而其他基本操作失败的情况。
集合更新可以被用于在ZooKeeper中构建需要保持全局一致性的数据结构,例如构建一个无向图。在ZooKeeper中用一个znode来表示无向图中的一个顶点,为了在两个顶点之间添加或删除一条边,我们需要同时更新两个顶点所分别对应的两个znode,因为每个znode中都有指向对方的引用。如果我们只用ZooKeeper的基本操作来实现边的更新,可能会让其他客户端发现无向图处于不一致的状态,即一个顶点具有指向另一个顶点的引用而对方却没有对应的应用。将针对两个znode的更新操作集合到一个multi操作中可以保证这组更新操作的原子性,也就保证了一对顶点之间不会出现不完整的连接。

2. 关于API

对于ZooKeeper客户端来说,主要有两种语言绑定(binding)可以使用:Java和C;当然也可以使用Perl、Python和REST的contrib绑定。对于每一种绑定语言来说,在执行操作时都可以选择同步执行或异步执行。看exists的签名
public Stat exists(final String path, Watcher watcher) throws KeeperException, InterruptedException
它返回一个封装有znode元数据的Stat对象(如果znode不存在,则返回null)
异步执行的签名如下

public void exists(final String path, Watcher watcher, StatCallback cb, Object ctx)

因为所有操作的结果都是通过回调来传送的,因此在Java API中异步方法的放回类型都是void。调用者传递一个回调的实现,当ZooKeeper相应时,该回调方法被调用。这种情况下,回调采用StatCallback接口,它有以下方法:

/**
* 回调
* @param rc 返回代码,对应于KeeperException的代码。每个非零代码都代表一个异常
* @param path 对应于客户端传递给exists方法的参数,用于识别这个回调所相应的请求。当path参数不能提供足够的信息时,客户端可以通过ctx参数来区分不同请求。如果path参数提供了足够的信息,ctx可以设为null
* @param ctx 对应于客户端传递给exists方法的参数,用于识别这个回调所相应的请求。可以是任意对象
* @param stat 这种情况下,stat参数是null
*/
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println("rc:" + rc);
System.out.println("path:" + path);
System.out.println("ctx:" + ctx);
}

异步API允许以流水线方式处理请求,这在某些情况下可以提供更好的吞吐量。

3. 观察触发器

在exists、getChildren和getData这些读操作上可以设置观察,这些观察可以被写操作create、delete和setData触发。ACL相关的操作不参与触发任何观察。当一个观察被触发时会产生一个观察事件,这个观察和触发它的操作共同决定着观察事件的类型。
当做观察的znode被创建、删除或其数据被更新时,设置在exists操作上的观察将被触发。
当所观察的znode被删除或其数据被更新时,设置在getData操作上的观察将被触发。创建znode不会触发getData操作上的观察,因为getData操作成功执行的前提是znode必须已经存在。
当所观察的znode的一个子节点被创建或删除时,或所观察的znode自己被删除时,设置在getChildren操作上的观察将会被触发。可以通过观察时间的类型来判断被删除的是znode还是其子节点:NodeDelete类型代表znode被删除;NodeChildrenChanged类型代表一个子节点被删除。

一个观察事件中包含涉及该事件的znode的路径,因此对于NodeCreated和NodeDeleted事件来说,可通过路径来判断哪一个节点被创建或删除。为了能够在NodeChildrenChanged事件发生之后判断是哪些子节点被修改,需要重新调用getChildren来获取新的子节点列表。与之类似,为了能够在NodeDataChanged事件之后获取新的数据,需要调用getData。

测试

package com.zhen.zookeeper.existsAndWatcher;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.AsyncCallback.StatCallback;
import org.apache.zookeeper.CreateMode;
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.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat; /**
* @author FengZhen
* @date 2018年10月13日
* exists与观察者
* state=-112 会话超时状态
state= -113 认证失败状态
state=  1 连接建立中
state= 2 (暂时不清楚如何理解这个状态,ZOO_ASSOCIATING_STATE)
state=3 连接已建立状态
state= 999 无连接状态 type=1 创建节点事件
type=2 删除节点事件
type=3 更改节点事件
type=4 子节点列表变化事件
type= -1 会话session事件
type=-2 监控被移除事件 */
public class ExistsAndWatcher implements Watcher{ private static final int SESSION_TIMEOUT = 5000; private ZooKeeper zk;
private CountDownLatch connectedSignal = new CountDownLatch(1); public void connect(String hosts) throws IOException, InterruptedException {
/**
* hosts:ZooKeeper服务的主机地址(可指定端口,默认是2181)
* SESSION_TIMEOUT:以毫秒为单位的会话超时参数(此处为5秒)
* this:Watcher对象的实例。Watcher对象接收来自于ZooKeeper的回调,以获得各种事件的通知。
*/
zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);
connectedSignal.await();
} /**
* 当客户端已经与ZK建立连接后,Watcher的process方法会被调用
* 参数是一个用于表示该连接的事件。
*/
public void process(WatchedEvent event) {
int type = event.getType().getIntValue();
System.out.println("watchedEven--" + event.getState().getIntValue() + " : " + type);
//连接事件
if (event.getState() == KeeperState.SyncConnected) {
/**
* 通过调用CountDownLatch的countDown方法来递减它的计数器。
* 锁存器(latch)被创建时带有一个值为1的计数器,用于表示在它释放所有等待线程之前需要发生的事件数。
* 在调用一次countDown方法之后,计数器的值变为0,则await方法返回。
*/
connectedSignal.countDown();
}
//如果为创建或者删除znode的话,需要再添加一个观察者,观察后续操作
if (type == 1 || type == 2) {
existsAndWatcher("/zoo");
}
} public void create(String groupName) throws KeeperException, InterruptedException {
String path = "/" + groupName;
/**
* 用ZK的create方法创建一个新的ZK的znode
* path:路径(用字符串表示)
* null:znode的内容(字节数组,此处为空值)
* Ids.OPEN_ACL_UNSAFE:访问控制列表(简称ACL,此处为完全开放的ACL,允许任何客户端对znode进行读写)
* CreateMode.PERSISTENT:znode类型
* znode类型可以分为两种:1.短暂的(ephemeral) 2.持久的(persistent)
* 创建znode的客户端断开连接时,无论客户端是明确断开还是因为任何原因而终止,短暂znode都会被ZK服务删除。持久znode不会被删除。
* create方法的返回值是ZK所创建的节点路径
*/
String createdPath = zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("Created " + createdPath);
} public void close() throws InterruptedException {
zk.close();
} public boolean exists(String path) throws KeeperException, InterruptedException {
Stat exists = zk.exists(path, true);
return null != exists;
} public void existsAndWatcher(String path) {
zk.exists(path, this, new StatCallback() {
/**
* 回调
* @param rc 返回代码,对应于KeeperException的代码。每个非零代码都代表一个异常
* @param path 对应于客户端传递给exists方法的参数,用于识别这个回调所相应的请求。当path参数不能提供足够的信息时,客户端可以通过ctx参数来区分不同请求。如果path参数提供了足够的信息,ctx可以设为null
* @param ctx 对应于客户端传递给exists方法的参数,用于识别这个回调所相应的请求。可以是任意对象
* @param stat 这种情况下,stat参数是null
*/
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println("rc:" + rc);
System.out.println("path:" + path);
System.out.println("ctx:" + ctx);
}
}, "标记回调所相应的请求");
} public void delete(String groupName) {
String path = "/" + groupName;
try {
List<String> children = zk.getChildren(path, false);
for (String child : children) {
zk.delete(path + "/" + child, -1);
}
/**
* delete方法有两个参数
* path:节点路径
* -1:版本号
* 如果所提供的版本号与znode的版本号一致,ZK会删除这个znode。
* 这是一种乐观的加锁机制,使客户端能够检测出对znode的修改冲突。
* 通过将版本号设置为-1,可以绕过这个版本检测机制,不管znode的版本号是什么而直接将其删除。
* ZK不支持递归删除,因此在删除父节点之前必须先删除子节点
*/
zk.delete(path, -1);
} catch (KeeperException e) {
System.out.printf("Group %s does not exist\n", groupName);
System.exit(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
String hosts = "localhost:2181";
String groupName = "zoo";
ExistsAndWatcher existsAndWatcher = new ExistsAndWatcher();
existsAndWatcher.connect(hosts);
//同步
boolean exists = existsAndWatcher.exists("/zoo");
System.out.println("exists zoo:" + exists);
//异步
existsAndWatcher.existsAndWatcher("/zoo");
existsAndWatcher.create(groupName);
existsAndWatcher.delete(groupName); existsAndWatcher.close();
} }

  

4. ACL列表

每个znode被创建时都会带有一个ACL列表,用于决定谁可以对它执行何种操作。
ACL依赖于ZooKeeper的客户端身份验证机制。ZooKeeper提供了以下几种身份验证方式
 Digest:通过用户名和密码来识别客户端
 Sasl:通过Kerberos来识别客户端
 Ip:通过客户端的IP地址来识别客户端

在建立一个ZooKeeper会话之后,客户端可以对自己进行身份验证。虽然znode的ACL列表会要求所有的客户端是经过验证的,但ZooKeeper的身份验证过程却是可选的,客户端必须自己进行身份验证来支持对znode的访问。
使用digest方式进行身份验证的例子
zk.addAuthInfo("digest", "fz:secret".getBytes());
每个ACL都是身份验证方式、符合该方式的一个身份和一组权限的组合。例如,如果打算给IP地址为10.0.0.1的客户端对某个znode的读权限,可以使用IP验证方式、10.0.0.1和READ权限在该znode上设置一个ACL。

测试

package com.zhen.zookeeper.ACL;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Perms; /**
* @author FengZhen
* @date 2018年11月25日
* ACL
*/
public class ACLTest implements Watcher{ private static final int SESSION_TIMEOUT = 5000; private ZooKeeper zk;
private CountDownLatch connectedSignal = new CountDownLatch(1); public void connect(String hosts) throws IOException, InterruptedException {
/**
* hosts:ZooKeeper服务的主机地址(可指定端口,默认是2181)
* SESSION_TIMEOUT:以毫秒为单位的会话超时参数(此处为5秒)
* this:Watcher对象的实例。Watcher对象接收来自于ZooKeeper的回调,以获得各种事件的通知。
*/
zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);
//根据IP
zk.addAuthInfo("ip", "192.168.1.103".getBytes());
//根据用户密码
zk.addAuthInfo("digest", "fz:123456".getBytes());
connectedSignal.await();
} /**
* 当客户端已经与ZK建立连接后,Watcher的process方法会被调用
* 参数是一个用于表示该连接的事件。
*/
public void process(WatchedEvent event) {
//连接事件
if (event.getState() == KeeperState.SyncConnected) {
/**
* 通过调用CountDownLatch的countDown方法来递减它的计数器。
* 锁存器(latch)被创建时带有一个值为1的计数器,用于表示在它释放所有等待线程之前需要发生的事件数。
* 在调用一次countDown方法之后,计数器的值变为0,则await方法返回。
*/
connectedSignal.countDown();
}
} public void createACLIP(String groupName) throws KeeperException, InterruptedException {
String path = "/" + groupName;
/**
* 用ZK的create方法创建一个新的ZK的znode
* path:路径(用字符串表示)
* null:znode的内容(字节数组,此处为空值)
* Ids.OPEN_ACL_UNSAFE:访问控制列表(简称ACL,此处为完全开放的ACL,允许任何客户端对znode进行读写)
* CreateMode.PERSISTENT:znode类型
* znode类型可以分为两种:1.短暂的(ephemeral) 2.持久的(persistent)
* 创建znode的客户端断开连接时,无论客户端是明确断开还是因为任何原因而终止,短暂znode都会被ZK服务删除。持久znode不会被删除。
* create方法的返回值是ZK所创建的节点路径
*/
//添加权限,设置IP
ACL aclIP = new ACL(Perms.ALL, new Id("ip", "192.168.1.103"));
System.out.println(aclIP);
List<ACL> acls = new ArrayList<ACL>();
acls.add(aclIP);
String createdPath = zk.create(path, null, acls, CreateMode.PERSISTENT);
System.out.println("Created " + createdPath);
} public void createACLDigest(String groupName) throws KeeperException, InterruptedException, NoSuchAlgorithmException {
String path = "/" + groupName;
/**
* 用ZK的create方法创建一个新的ZK的znode
* path:路径(用字符串表示)
* null:znode的内容(字节数组,此处为空值)
* Ids.OPEN_ACL_UNSAFE:访问控制列表(简称ACL,此处为完全开放的ACL,允许任何客户端对znode进行读写)
* CreateMode.PERSISTENT:znode类型
* znode类型可以分为两种:1.短暂的(ephemeral) 2.持久的(persistent)
* 创建znode的客户端断开连接时,无论客户端是明确断开还是因为任何原因而终止,短暂znode都会被ZK服务删除。持久znode不会被删除。
* create方法的返回值是ZK所创建的节点路径
*/
//添加权限,设置IP
ACL aclIP = new ACL(Perms.ALL, new Id("digest", DigestAuthenticationProvider.generateDigest("fz:123456")));
System.out.println(aclIP);
List<ACL> acls = new ArrayList<ACL>();
acls.add(aclIP);
String createdPath = zk.create(path, null, acls, CreateMode.PERSISTENT);
System.out.println("Created " + createdPath);
} public void close() throws InterruptedException {
zk.close();
} public void writeZnodeACLDigest(String groupName) throws KeeperException, InterruptedException {
String path = "/" + groupName;
zk.setData(path, "test_digest_data".getBytes(), 0);
} public void writeZnodeACLIP(String groupName) throws KeeperException, InterruptedException {
String path = "/" + groupName;
zk.setData(path, "test_ip_data".getBytes(), 0);
} public void readZnode(String groupName) throws KeeperException, InterruptedException {
String path = "/" + groupName;
String data = zk.getData(path, false, null).toString();
System.out.println("data = " + data);
} public static void main(String[] args) throws IOException, InterruptedException, KeeperException, NoSuchAlgorithmException {
String hosts = "192.168.1.103:2181";
String groupNameIP = "znode_acl_test_ip";
String groupNameDigest = "znode_acl_test_digest";
ACLTest aclTest = new ACLTest();
aclTest.connect(hosts);
//digest
// aclTest.createACLDigest(groupNameDigest);
// aclTest.writeZnodeACLDigest(groupNameDigest);
aclTest.readZnode(groupNameDigest); //IP
// aclTest.createACLIP(groupNameIP);
// aclTest.writeZnodeACLIP(groupNameIP);
// aclTest.readZnode(groupNameIP); aclTest.close(); //创建完带有ACL的znode之后,查看ACL
//[zk: localhost:2181(CONNECTED) 7] getAcl /znode_acl_test_ip
//'ip,'192.168.1.103
//: cdrwa //不设置IP直接读取该znode内容,报错如下
//Exception in thread "main" org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /znode_acl_test_ip } }

  

ZooKeeper服务-操作(API、集合更新、观察者、ACL)的更多相关文章

  1. zookeeper的java api操作

    zookeeper的java api操作 创建会话: Zookeeper(String connectString,int sessionTimeout,Watcher watcher) Zookee ...

  2. 【C#】分享基于Win32 API的服务操作类(解决ManagedInstallerClass.InstallHelper不能带参数安装的问题)

    注:这里的服务是指Windows 服务. ------------------201508250915更新------------------ 刚刚得知TransactedInstaller类是支持带 ...

  3. 使用Kazoo操作ZooKeeper服务治理

    单机服务的可靠性及可扩展性有限,某台服务宕机可能会影响整个系统的正常使用:分布式服务能够有效地解决这一问题,但同时分布式服务也会带来一些新的问题,如:服务发现(新增或者删除了服务如何确保能让客户端知道 ...

  4. Zookeeper 面试题(持续更新、吐血推荐)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...

  5. 【分布式】Zookeeper使用--Java API

    一.前言 上一篇博客我们通过命令行来操作Zookeper的客户端和服务端并进行相应的操作,这篇主要介绍如何通过API(JAVA)来操作Zookeeper. 二.开发环境配置 首先打开Zookeeper ...

  6. 【6】Zookeeper脚本及API

    一.客户端脚本 1.1.客户端连接 cd /usr/local/services/zookeeper/zookeeper-3.4.13/bin ##连接本地Zookeeper服务器 sh zkCli. ...

  7. ZooKeeper学习第三期---Zookeeper命令操作

    一.Zookeeper的四字命令 Zookeeper支持某些特定的四字命令字母与其的交互.他们大多数是查询命令,用来获取Zookeeper服务的当前状态及相关信息.用户在客户端可以通过telnet或n ...

  8. 【Zookeeper系列】Zookeeper命令操作(转)

    原文链接:https://www.cnblogs.com/sunddenly/p/4031881.html 一.Zookeeper的四字命令 Zookeeper支持某些特定的四字命令字母与其的交互.他 ...

  9. 5、Zookeeper命令操作

    一.Zookeeper的四字命令 Zookeeper支持某些特定的四字命令字母与其的交互.他们大多数是查询命令,用来获取Zookeeper服务的当前状态及相关信息.用户在客户端可以通过telnet或n ...

随机推荐

  1. jmeter通过json extrcator或者正则表达式获取json返回信息

    1.下载地址,及插件文档资料 https://jmeter-plugins.org/wiki/JSONPathExtractor/ json信息如下 { "error_code": ...

  2. 哈工大LTP

    http://ltp.ai/ http://pyltp.readthedocs.io/zh_CN/latest/ http://www.cnblogs.com/Denise-hzf/p/6612886 ...

  3. Python学习笔记(三)windows下安装theano

    2016.6.28补充: 不论是实验室的电脑还是我的笔记本,只要是windows下,theano.test()都是不通过的.虽然能使用一些theano中的函数,但是我感觉很不好. 所以还是转Ubunt ...

  4. coursera 《现代操作系统》 -- 第四周 处理器调度

    优先级反转 这往往出现在一个高优先级任务等待访问一个被低优先级任务正在使用的临界资源,从而阻塞了高优先级任务:同时,该低优先级任务被一个次高优先级的任务所抢先,从而无法及时地释放该临界资源.这种情况下 ...

  5. 打日志--以python为例

    日志报错要去修,要不然是隐患,总有一天会爆炸 增加日志是排错的好方法,不要不舍得加日志,比如怕代码变难看,怕日志输出太多. python logging exc_info sys.exc_info() ...

  6. 我的Android进阶之旅------>Android中查看应用签名信息

    一.查看自己的证书签名信息 如上一篇文章<我的Android进阶之旅------>Android中制作和查看自定义的Debug版本Android签名证书>地址:http://blog ...

  7. ShutIt:一个基于 Python 的 shell 自动化框架

    ShutIt是一个易于使用的基于shell的自动化框架.它对基于python的expect库(pexpect)进行了包装.你可以把它看作是“没有痛点的expect”.它可以通过pip进行安装. Hel ...

  8. python 中 for使用小技巧

    testDict = {i: i * i for i in xrange(10)} testSet = {i * 2 for i in xrange(10)} print(testSet) print ...

  9. new AnnotationConfigApplicationContext(MyBean.class)时,发生了什么?

    当我们run一段代码,像下面这样两行.spring究竟做了什么些,让整个容器准备就绪,交付给用户直接可用的各种特性.为了弄清楚,默默梳理记录下来. public static void main (S ...

  10. Kafka的架构

    1.Kafka整体架构    一个典型的Kafka集群中包含若干producer(可以是web前端产生的page view,或者是服务器日志,系统CPU.memory等),若干broker(Kafka ...