数据结构论坛

首页 » 分类 » 定义 » Zookeeper的基本概念
TUhjnbcbe - 2025/4/9 20:50:00
皮肤科医生雷安萍 https://disease.39.net/bjzkbdfyy/250401/m2vcw9u.html

ZooKeeper以一种类似于文件系统的树形数据结构实现名称空间。名称空间中的每个节点都是一个znode。znode和文件系统的路径不一样,在文件系统中,路径只是一个名称,不包含数据。而znode不仅是一个路径,还携带数据。

ZooKeeper数据树结构示例

需要注意,虽然是树形数据结构,但ZooKeeper是内存数据库,节点的信息全都存放在内存中(在写操作达到一定次数后,会对内存数据库拍快照,将其序列化到磁盘上),所以在文件系统中是看不到这个树形结构的,不过可以借助ZooKeeper的第三方web工具来查看。

除了数据外,znode还管理了其他一些元数据,存储在stat对象中:

版本号:znode的数据每次更新时,该版本号递增。当客户端请求该znode时,数据和版本号会一起发回。另外,当znode重建时版本号会被重置,这似乎很自然,但很多时候这是巨坑的来源,比如:客户端执行了’set/test“testdata”1’,即指定版本1写/test节点,之后该znode被删除重建,数据默认置为“”,版本号还是1,此时客户端请求时虽然该版本号仍然存在,但已经是错误的数据了。

ACL:即AccessControlList,用来限定哪些账号可以操作该znode。ZooKeeper内置了4种ACL模式,第1种是any,即不鉴权;第2种是super,即不受任何ACL约束的管理员模式;第3种是digest,使用用户名和密码进行认证;第4种是SASL,通常使用Kerberos协议来认证,但Kerberos也是个大坑,并且对性能也有一定影响。

时间戳:其实就是zxid,存储该znode创建或修改时的时间戳,用于缓存验证或协调更新等。

znode的属性

ZooKeeper命名空间中的每个znode都有一个与之关联的stat结构,类似于Unix/Linux文件系统中文件的stat结构。znode的stat结构中的字段显示如下,各自的含义如下:

cZxid:这是导致创建znode更改的事务ID。

mZxid:这是最后修改znode更改的事务ID。

pZxid:这是用于添加或删除子节点的znode更改的事务ID。

ctime:表示从-01-01T00:00:00Z开始以毫秒为单位的znode创建时间。

mtime:表示从-01-01T00:00:00Z开始以毫秒为单位的znode最近修改时间。

dataVersion:表示对该znode的数据所做的更改次数。

cversion:这表示对此znode的子节点进行的更改次数。

aclVersion:表示对此znode的ACL进行更改的次数。

ephemeralOwner:如果znode是ephemeral类型节点,则这是znode所有者的sessionID。如果znode不是ephemeral节点,则该字段设置为零。

dataLength:这是znode数据字段的长度。

numChildren:这表示znode的子节点的数量。

操作znode

ZooKeeper的API暴露了以下方法:

create/pathdata

创建一个名为/path的znode节点,并包含数据data。

delete/path

删除名为/path的znode。

exists/path

检查是否存在名为/path的节点。

setData/pathdata

设置名为/path的znode的数据为data。

getData/path

返回名为/path节点的数据信息。

getChildren/path

返回所有/path节点的所有子节点列表。

需要注意的是,ZooKeeper并不允许局部写入或读取znode节点的数据。当设置一个znode节点的数据或读取时,znode节点的内容会被整个替换或全部读取进来。

znode的不同类型

当新建znode时,还需要指定该节点的类型(mode),不同的类型决定了znode节点的行为方式。

持久节点和临时节点

znode节点可以是持久(persistent)节点,还可以是临时(ephemeral)节点。持久的znode,如/path,只能通过调用delete来进行删除。临时的znode与之相反,当创建该节点的客户端崩溃或关闭了与ZooKeeper的连接时,这个节点就会被删除。

持久znode是一种非常有用的znode,可以通过持久类型的znode为应用保存一些数据,即使znode的创建者不再属于应用系统时,数据也可以保存下来而不丢失。例如,在主-从模式例子中,需要保存从节点的任务分配情况,即使分配任务的主节点已经崩溃了。

临时znode传达了应用某些方面的信息,仅当创建者的会话有效时这些信息必须有效保存。例如,在主从模式的例子中,当主节点创建的znode为临时节点时,该节点的存在意味着现在有一个主节点,且主节点状态处于正常运行中。如果主znode消失后,该znode节点仍然存在,那么系统将无法监测到主节点崩溃。这样就可以阻止系统继续进行,因此这个znode需要和主节点一起消失。我们也在从节点中使用临时的znode,如果一个从节点失效,那么会话将会过期,之后znode/workers也将自动消失。

一个临时znode,在以下两种情况下将会被删除:

1.当创建该znode的客户端的会话因超时或主动关闭而中止时。

2.当某个客户端(不一定是创建者)主动删除该节点时。

因为临时的znode在其创建者的会话过期时被删除,所以我们现在不允许临时节点拥有子节点。

有序节点

一个znode还可以设置为有序(sequential)节点。一个有序znode节点被分配唯一个单调递增的整数。当创建有序节点时,一个序号会被追加到路径之后。例如,如果一个客户端创建了一个有序znode节点,其路径为/tasks/task-,那么ZooKeeper将会分配一个序号,如1,并将这个数字追加到路径之后,最后该znode节点为/tasks/task-1。有序znode通过提供了创建具有唯一名称的znode的简单方式。同时也通过这种方式可以直观地查看znode的创建顺序。总之,znode一共有4种类型:

持久的(persistent)

临时的(ephemeral)

持久有序的(persistent_sequential)

临时有序的(ephemeral_sequential)

监视与通知

ZooKeeper通常以远程服务的方式被访问,如果每次访问znode时,客户端都需要获得节点中的内容,这样的代价就非常大。因为这样会导致更高的延迟,而且ZooKeeper需要做更多的操作。考虑下图的例子,第二次调用getChildren/tasks返回了相同的值,一个空的集合,其实是没有必要的。

同一个znode的多次读取

这是一个常见的轮询问题。为了替换客户端的轮询,我们选择了基于通知(notification)的机制:客户端向ZooKeeper注册需要接收通知的znode,通过对znode设置监视点(watch)来接收通知。监视点是一个单次触发的操作,意即监视点会触发一个通知。为了接收多个通知,客户端必须在每次通知后设置一个新的监视点。在下图的情况下,当节点/tasks发生变化时,客户端会收到一个通知,并从ZooKeeper读取一个新值。

使用通知机制来获悉znode的变化

当使用通知机制时,还有一些需要知道的事情。因为通知机制是单次触发的操作,所以在客户端接收一个znode变更通知并设置新的监视点时,znode节点也许发生了新的变化(不要担心,你不会错过状态的变化)。让我们看一个例子来说明它到底是怎么工作的。假设事件按以下顺序发生:

1.客户端c1设置监视点来监控/tasks数据的变化。

2.客户端c1连接后,向/tasks中添加了一个新的任务。

3.客户端c1接收通知。

4.客户端c1设置新的监视点,在设置完成前,第三个客户端c3连接后,向/tasks中添加了一个新的任务。

客户端c1最终设置了新的监视点,但由c3添加数据的变更并没有触发一个通知。为了观察这个变更,在设置新的监视点前,c1实际上需要读取节点/tasks的状态,通过在设置监视点前读取ZooKeeper的状态,最终,c1就不会错过任何变更。

ZooKeeper可以定义不同类型的通知,这依赖于设置监视点对应的通知类型。客户端可以设置多种监视点,如监控znode的数据变化、监控znode子节点的变化、监控znode的创建或删除。为了设置监视点,可以使用任何API中的调用来读取ZooKeeper的状态,在调用这些API时,传入一个watcher对象或使用默认的watcher。

版本

每一个znode都有一个版本号,它随着每次数据变化而自增。两个API操作可以有条件地执行:setData和delete。这两个调用以版本号作转入参数,只有当转入参数的版本号与服务器上的版本号一致时调用才会成功。当多个ZooKeeper客户端对同一个znode进行操作时,版本的使用就会显得尤为重要。例如,假设客户端c1对znode/config写入了一些配置信息,如果另一个客户端c2同时更新了这个znode,此时c1的版本号已经过期,c1调用setData一定不会成功。使用版本机制有效避免了以上情况。在这个例子中,c1在写入数据时使用的版本无法匹配,使得操作失败,下描述了这个情况。

使用版本来阻止并行操作的不一致性

1
查看完整版本: Zookeeper的基本概念