操作 Operations

下面的表格中列出了9种ZooKeeper的操作。

操作说明
createCreates a znode (the parent znode must already exist)
deleteDeletes a znode (the znode must not have any children)
existsTests whether a znode exists and retrieves its metadata
getACL, setACLGets/sets the ACL for a znode
getChildrenGets a list of the children of a znode
getData, setDataGets/sets the data associated with a znode
syncSynchronizes a client’s view of a znode with ZooKeeper

调用deletesetData操作时,我们必须指定一个znode版本号(version number),即我们必须指定我们要删除或者更新znode数据的哪个版本。如果版本号不匹配,操作将会失败。失败的原因可能是在我们提交之前,该znode已经被修改过了,版本号发生了增量变化。那么我们该怎么办呢?我可以考虑重试,或者调用其他的操作。例如,我们提交更新失败后,可以重新获取znode当前的数据,看看当前的版本号是什么,再做更新操作。

ZooKeeper虽然可以被看作是一个文件系统,但是由于ZooKeeper文件很小,所以没有提供像一般文件系统所提供的openclose或者seek操作。

注意
这里的sync操作与POSIX文件系统的fsync()操作是不同的。就像我们早前讲过的,ZooKeeper的写操作是原子性的,一个成功的写操作只保证数据被持久化到大多数ZooKeeper的服务器存储上。所以读操作可能会读取不到最新状态的数据,sync操作用来让client强制所访问的ZooKeeper服务器上的数据状态更新到最新状态。我们会在《一致性 Consistentcy》一节中详细介绍。

批量更新 Multiupdate

ZooKeeper支持将一些原始的操作组合成一个操作单元,然后执行这些操作。那么这种批量操作也是具有原子性的,只可能有两种执行结果,成功和失败。批量操作单元中的操作,不会出现一些操作执行成功,一些操作执行失败的情况,即要么都成功,要么都失败。

Multiupdate对于绑定一些结构化的全局变量很有用处。例如绑定一个无向图(undirected graph)。无向图的顶点(vertex)由znode来表示。添加和删除边(edge)的操作,由修改边的两个关联znode来实现。如果我们使用ZooKeeper的原始的操作来实现对边(edge)的操作,那么就有可能产生两个znode修改不一致的情况(一个修改成功,一个修改失败)。那么我们将修改两个znode的操作放入到一个Multi修改单元中,就能够保证两个znode,要么都修改成功,要么都修改失败。这样就能够避免修改无向图的边时产生修改不一致的现象。

APIs

ZooKeeper客户端使用的核心编程语言有JAVA和C;同时也支持Perl、Python和REST。执行操作的方式呢,分为同步执行和异步执行。我们之前已经见识过了同步的Java API中的exists

  1. public Stat exists(String path, Watcher watcher) throws KeeperException,
  2. InterruptedException

下面代码则是异步方式的exists:

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

Java API中,异步的方法的返回类型都是void,而操作的返回的结果将传递到回调对象的回调函数中。回调对象将实现StatCallback接口中的一个回调函数,来接收操作返回的结果。函数接口如下:

  1. public void processResult(int rc, String path, Object ctx, Stat stat);

参数rc表示返回码,请参考KeeperException中的定义。在stat参数为null的情况下,非0的值表示一种异常。参数pathctx与客户端调用的exists方法中的参数相等,这两个参数通常用来确定回调中获得的响应是来至于哪个请求的。参数ctx可以是任意对象,只有当path参数不能消灭请求的歧义时才会用到。如果不需要参数ctx,可以设置为null。

应该使用同步API还是异步API呢?
两种API提供了相同的功能,需要使用哪种API取决于你程序的模式。例如,你设计的程序模式是一个事件驱动模式的程序,那么你最好使用异步API。异步API也可以被用在追求一个比较好的数据吞吐量的场景。想象一下,如果你需要得去大量的znode数据,并且依靠独立的进程来处理他们。如果使用同步API,每次读取操作都会被阻塞住,直到返回结果。不如使用异步API,读取操作可以不必等待返回结果,继续执行。而使用另外的线程来处理返回结果。

观察模式触发器 Watch triggers

读操作,例如:existsgetChildrengetData会在znode上开启观察模式,并且写操作会触发观察模式事件,例如:createdeletesetData。ACL(Access Control List)操作不会启动观察模式。观察模式被触发时,会生成一个事件,这个事件的类型取决于触发他的操作:

  • exists启动的观察模式,由创建znode,删除znode和更新znode操作来触发。

  • getData启动的观察模式,由删除znode和更新znode操作触发。创建znode不会触发,是因为getData操作成功的前提是znode必须已经存在。

  • getChildren启动的观察模式,由子节点创建和删除,或者本节点被删除时才会被触发。我们可以通过事件的类型来判断是本节点被删除还是子节点被删除:NodeChildrenChanged表示子节点被删除,而NodeDeleted表示本节点删除。

—-Watch trigger
Watch creationcreate znodecreate childdelete znodedelete childsetData
existsNodeCreated-NodeDeleted-NodeDataChanged
getData--NodeDeleted-NodeDataChanged
getChildren-getChildrenNodeDeletedNodeChildrenChanged-

事件包含了触发事件的znode的path,所以我们通过NodeCreatedNodeDeleted事件就可以知道哪个znode被创建了或者删除了。如果我们需要在NodeChildrenChanged事件发生后知道哪个子节点被改变了,我们就需要再调用一次getChildren来获得一个新的子节点列表。与之类似,在NodeDataChanged事件发生后,我们需要调用getData来获得新的数据。我们在编写程序时,会在接收到事件通知后改变znode的状态,所以我们一定要清楚的记住znode的状态变化。

ACLs 访问控制操作

znode的创建时,我们会给他一个ACL(Access Control List),来决定谁可以对znode做哪些操作。

ZooKeeper通过鉴权来获得客户端的身份,然后通过ACL来控制客户端的访问。鉴权方式有如下几种:

  • digest

使用用户名和密码方式

  • sasl

使用Kerberos鉴权

  • ip

使用客户端的IP来鉴权

客户端可以在与ZooKeeper建立会话连接后,自己给自己授权。授权是并不是必须的,虽然znode的ACL要求客户端必须是身份合法的,在这种情况下,客户端可以自己授权来访问znode。下面的例子,客户端使用用户名和密码为自己授权:

  1. zk.addAuthInfo("digest", "tom:secret".getBytes());

ACL是由鉴权方式、鉴权方式的ID和一个许可(permession)的集合组成。例如,我们想通过一个ip地址为10.0.0.1的客户端访问一个znode。那么,我们需要为znode设置一个ACL,鉴权方式使用IP鉴权方式,鉴权方式的ID为10.0.0.1,只允许读权限。使用JAVA我们将像如下方式创建一个ACL对象:

  1. new ACL(Perms.READ,new Id("ip", "10.0.0.1"));

所有的许可权限将在下表中列出。请注意,exists操作不受ACL的控制,所以任何一个客户端都可以通过exists操作来获得任何znode的状态,从而得知znode是否真的存在。

ACL permissionPermitted operations
CREATEcreate (a child znode)
READgetChildren,getData
WRITEsetData
DELETEdelete (a child znode)
ADMINsetACL

ZooDefs.Ids类中,有一些ACL的预定义变量,包括OPEN_ACL_UNSAFE,这个设置表示将赋予所有的许可给客户端(除了ADMIN的许可)。

另外,我们可以使用ZooKeeper鉴权的插件机制,来整合第三方的鉴权系统。