ZooKeeper程序员指南

开发使用ZooKeeper的分布式应用程序

  • 介绍
  • ZooKeeper数据模型
    • ZNodes

      • 手表
      • 数据访问
      • 短暂的节点
      • 序列节点 - 唯一命名
      • 容器节点
      • TTL节点
    • 在ZooKeeper的时间
    • ZooKeeper统计结构
  • ZooKeeper会话
  • ZooKeeper手表
    • 手表的语义
    • 删除手表
    • ZooKeeper对手表的保证
    • 关于手表的事情
  • 使用ACL的ZooKeeper访问控制
    • ACL权限

      • 内置ACL方案
      • ZooKeeper C客户端API
  • 可插入ZooKeeper身份验证
  • 一致性保证
  • 绑定
    • Java绑定

      • 客户端配置参数
    • C绑定
      • 安装
      • 建立自己的C客户端
  • 构建块:ZooKeeper操作指南
    • 处理错误
    • 连接到ZooKeeper
    • 阅读操作
    • 写操作
    • 处理手表
    • 错误的ZooKeeper操作
  • 程序结构,简单示例
  • 陷阱:常见问题和故障排除

介绍

本文档是希望创建利用ZooKeeper协调服务的分布式应用程序的开发人员的指南。它包含概念和实用信息。

本指南的前四部分介绍了各种ZooKeeper概念的更高层次讨论。这些对于理解ZooKeeper如何工作以及如何使用它们都是必要的。它不包含源代码,但它确实熟悉与分布式计算相关的问题。第一组中的部分是:

  • ZooKeeper数据模型
  • ZooKeeper会话
  • ZooKeeper手表
  • 一致性保证

接下来的四节提供了实用的编程信息。这些是:

  • 构建块:ZooKeeper操作指南
  • 绑定
  • 程序结构,简单示例 [tbd]
  • 陷阱:常见问题和故障排除

本书最后附有一个附录,其中包含与其他有用的ZooKeeper相关信息的链接。

本文档中的大部分信息都是作为独立的参考资料编写的。但是,在开始第一个ZooKeeper应用程序之前,您应该至少阅读ZooKeeper数据模型和ZooKeeper基本操作的章节。此外,简单编程示例 [tbd]有助于理解ZooKeeper客户端应用程序的基本结构。

ZooKeeper数据模型

ZooKeeper有一个分层名称空间,很像分布式文件系统。唯一的区别是命名空间中的每个节点都可以包含与之关联的数据以及子节点。这就像拥有一个允许文件也是目录的文件系统。节点的路径始终表示为规范,绝对,斜线分隔的路径; 没有相对的参考。任何unicode字符都可以在受以下约束限制的路径中使用:

  • 空字符(\ u0000)不能是路径名的一部分。(这会导致C绑定出现问题。)
  • 无法使用以下字符,因为它们无法正常显示或以令人困惑的方式呈现:\ u0001 - \ u001F和\ u007F
    • \ u009F。
  • 不允许使用以下字符:\ ud800 - uF8FF,\ uFFF0 - uFFFF。
  • “。” character可以用作另一个名称的一部分,但是“。” 并且“...”不能单独用于表示沿路径的节点,因为ZooKeeper不使用相对路径。以下内容无效:“/ a / b /。/ c”或“/a/b/../c”。
  • 令牌“zookeeper”被保留。

ZNodes

ZooKeeper树中的每个节点都称为 znode。Znodes维护一个stat结构,包括数据更改的版本号,acl更改。stat结构也有时间戳。版本号与时间戳一起允许ZooKeeper验证缓存并协调更新。每次znode的数据更改时,版本号都会增加。例如,每当客户端检索数据时,它也会接收数据的版本。当客户端执行更新或删除时,它必须提供正在更改的znode的数据版本。如果它提供的版本与实际版本的数据不匹配,则更新将失败。(可以覆盖此行为。有关详细信息,请参阅...)[tbd ...]

######注意

在分布式应用程序工程中,单词 节点可以指通用主机,服务器,集合成员,客户端进程等。在ZooKeeper文档中,znodes指的是数据节点。 服务器指的是构成ZooKeeper服务的机器; 仲裁对等体是指构成整体的服务器; client指的是使用ZooKeeper服务的任何主机或进程。

Znodes是程序员访问的主要功能。它们有几个值得一提的特征。

手表

客户端可以在znodes上设置监视。对该znode的更改会触发手表,然后清除手表。当监视触发时,ZooKeeper会向客户端发送通知。有关手表的更多信息,请参阅ZooKeeper手表部分 。

数据访问

存储在命名空间中每个znode的数据以原子方式读取和写入。读取获取与znode关联的所有数据字节,写入替换所有数据。每个节点都有一个访问控制列表(ACL),限制谁可以做什么。

ZooKeeper并非设计为通用数据库或大型对象库。相反,它管理协调数据。这些数据可以以配置,状态信息,会合等形式出现。各种形式的协调数据的共同特性是它们相对较小:以千字节为单位。ZooKeeper客户端和服务器实现具有健全性检查,以确保znode的数据少于1M,但数据应远低于平均数据。在相对较大的数据大小上操作将导致某些操作比其他操作花费更多的时间并且将影响某些操作的延迟,因为通过网络将更多数据移动到存储介质上需要额外的时间。如果需要大数据存储,处理此类数据的通常模式是将其存储在大容量存储系统上,

短暂的节点

ZooKeeper也有短暂节点的概念。只要创建znode的会话处于活动状态,就会存在这些znode。会话结束时,znode将被删除。由于这种行为,短暂的znodes不允许有孩子。可以使用getEphemerals() api 检索会话的短暂列表。

getEphemerals()

检索由给定路径的会话创建的临时节点的列表。如果路径为空,它将列出会话的所有临时节点。 用例 - 如果需要收集会话的临时节点列表以进行重复数据输入检查,并且节点是按顺序方式创建的,则可能是示例用例,因此您不知道重复检查的名称。在这种情况下,getEphemerals()api可用于获取会话的节点列表。这可能是服务发现的典型用例。

序列节点 - 唯一命名

创建znode时,您还可以请求ZooKeeper在路径末尾附加一个单调递增的计数器。此计数器对于父znode是唯一的。计数器的格式为%010d - 即10位数,0(零)填充(计数器以这种方式格式化以简化排序),即“0000000001”。有关此功能的示例用法,请参阅 队列配方。注意:用于存储下一个序列号的计数器是由父节点维护的signed int(4bytes),当递增超过2147483647时,计数器将溢出(产生名称“-2147483648”)。

容器节点

在3.6.0中添加

ZooKeeper有容器znodes的概念。容器znodes是特殊用途的znode,可用于诸如leader,lock等配方。当删除容器的最后一个子容器时,容器将成为服务器在将来某个时候删除的候选容器。

给定此属性,您应该准备在容器znodes中创建子项时获取KeeperException.NoNodeException。即,当在容器内创建子znode时,znode始终检查KeeperException.NoNodeException并在发生时重新创建容器znode。

TTL节点

在3.6.0中添加

在创建PERSISTENT或PERSISTENT_SEQUENTIAL znode时,您可以选择为znode设置TTL(以毫秒为单位)。如果未在TTL中修改znode并且没有子节点,则它将成为服务器在将来的某个时刻删除的候选者。

注意:必须通过System属性启用TTL节点,因为默认情况下它们被禁用。有关详细信息,请参阅“ 管理员指南 ”。如果您尝试在没有设置正确的System属性的情况下创建TTL节点,则服务器将抛出KeeperException.UnimplementedException。

在ZooKeeper的时间

ZooKeeper以多种方式跟踪时间:

  • Zxid 对ZooKeeper状态的每次更改都会以zxid(ZooKeeper Transaction Id)的形式接收戳记。这暴露了ZooKeeper所有更改的总排序。每个更改都将具有唯一的zxid,如果zxid1小于zxid2,则zxid1发生在zxid2之前。
  • 版本号 对节点的每次更改都会导致该节点的版本号之一增加。三个版本号是版本(znode数据的更改次数),cversion(znode子项的更改次数)和aversion(znode的ACL更改次数)。
  • Ticks 当使用多服务器ZooKeeper时,服务器使用滴答来定义事件的时间,例如状态上载,会话超时,对等体之间的连接超时等。滴答时间仅通过最小会话超时(滴答时间的2倍)间接暴露。 ; 如果客户端请求的会话超时小于最小会话超时,则服务器将告诉客户端会话超时实际上是最小会话超时。
  • 实时 ZooKeeper根本不使用实时或时钟时间,除了在znode创建和znode修改时将时间戳放入stat结构。

ZooKeeper统计结构

ZooKeeper中每个znode的Stat结构由以下字段组成:

  • czxid 导致创建此znode的更改的zxid。
  • mzxid 最后修改此znode的更改的zxid。
  • pzxid 最后修改此znode的子项的更改的zxid。
  • ctime 创建此znode时从纪元开始的时间(以毫秒为单位)。
  • mtime 上次修改此znode时的时间(以毫秒为单位)。
  • version 对此znode数据的更改次数。
  • cversion 此znode的子项的更改数。
  • aversion 对此znode的ACL的更改次数。
  • ephemeralOwner 如果znode是一个临时节点,则此znode的所有者的会话ID。如果它不是短暂的节点,则它将为零。
  • dataLength 此znode的数据字段的长度。
  • numChildren 此znode的子节点数。

ZooKeeper会话

ZooKeeper客户端通过使用语言绑定创建服务句柄,与ZooKeeper服务建立会话。一旦创建,句柄就会以CONNECTING状态启动,客户端库会尝试连接到构成ZooKeeper服务的其中一个服务器,此时它将切换到CONNECTED状态。在正常操作期间将处于这两种状态之一。如果发生不可恢复的错误,例如会话到期或身份验证失败,或者应用程序显式关闭句柄,则句柄将移至CLOSED状态。下图显示了ZooKeeper客户端的可能状态转换:

要创建客户端会话,应用程序代码必须提供包含逗号分隔的host:port对列表的连接字符串,每个对应一个ZooKeeper服务器(例如“127.0.0.1:4545”或“127.0.0.1:3000,127.0.0.1”) :3001,127.0.0.1:3002" )。ZooKeeper客户端库将选择任意服务器并尝试连接到它。如果此连接失败,或者客户端因任何原因与服务器断开连接,客户端将自动尝试列表中的下一个服务器,直到(重新)建立连接。

在3.2.0中添加:可选的“chroot”后缀也可以附加到连接字符串。这将在解释相对于此根的所有路径时运行客户端命令(类似于unix chroot命令)。如果使用,示例将如下所示:“127.0.0.1:4545/app/a”或“127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a”,其中客户端将根植于“/ app / a”和所有路径都相对于这个根 - 即获取/设置/ etc ...“/ foo / bar”将导致操作在“/ app / a / foo / bar”上运行(来自服务器角度)。此功能在多租户环境中特别有用,其中特定ZooKeeper服务的每个用户可以以不同方式植入。这使得重用变得更加简单,因为每个用户都可以编写他/她的应用程序,就好像它是以“/”为根,

当客户端获取ZooKeeper服务的句柄时,ZooKeeper会创建一个ZooKeeper会话,表示为64位数字,并分配给客户端。如果客户端连接到不同的ZooKeeper服务器,它将发送会话ID作为连接握手的一部分。作为安全措施,服务器为任何ZooKeeper服务器可以验证的会话ID创建密码。当客户端建立会话时,密码将以会话ID发送到客户端。只要客户端使用新服务器重新建立会话,客户端就会使用会话ID发送此密码。

ZooKeeper客户端库调用创建ZooKeeper会话的参数之一是会话超时(以毫秒为单位)。客户端发送请求的超时,服务器响应它可以给客户端的超时。当前实现要求超时至少为tickTime的2倍(在服务器配置中设置),最多为tickTime的20倍。ZooKeeper客户端API允许访问协商的超时。

当客户端(会话)从ZK服务群集分区时,它将开始搜索在会话创建期间指定的服务器列表。最终,当重新建立客户端与至少一个服务器之间的连接时,会话将再次转换到“已连接”状态(如果在会话超时值内重新连接)或者它将转换到“已过期”状态(如果在会话超时后重新连接)。不建议在断开连接时创建新的会话对象(c绑定中的新ZooKeeper.class或zookeeper句柄)。ZK客户端库将为您处理重新连接。特别是我们在客户端库中内置了一些启发式方法来处理诸如“群体效应”之类的事情......

会话到期由ZooKeeper集群本身管理,而不是由客户端管理。当ZK客户端与群集建立会话时,它提供上面详述的“超时”值。群集使用此值来确定客户端会话何时到期。当群集在指定的会话超时期限内没有从客户端收到消息(即没有心跳)时,就会发生过期。在会话到期时,群集将删除该会话拥有的任何/所有短暂节点,并立即通知任何/所有连接的客户端该更改(任何人观看这些znode)。此时,过期会话的客户端仍然与群集断开连接,除非能够重新建立与群集的连接,否则不会通知会话过期。

过期会话的观察者看到的过期会话的状态转换示例:

  1. 'connected':建立会话,客户端与集群通信(客户端/服务器通信正常运行)
  2. ....客户端是从群集分区的
  3. 'disconnected':客户端已失去与群集的连接
  4. ....时间过去,在“超时”时间段之后,群集使会话到期,客户端看不到任何内容,因为它与群集断开连接
  5. ....时间流逝,客户端重新获得与群集的网络级连接
  6. 'expired':最终客户端重新连接到群集,然后通知过期

ZooKeeper会话建立调用的另一个参数是默认观察者。当客户端发生任何状态更改时,监视器会收到通知。例如,如果客户端失去与服务器的连接,则客户端将被通知,或者客户端的会话到期等等......此观察者应该考虑将初始状态断开(即在任何状态更改事件通过以下方式发送给观察者之前)客户端lib)。在新连接的情况下,发送给观察者的第一个事件通常是会话连接事件。

会话通过客户端发送的请求保持活动状态。如果会话空闲一段时间会使会话超时,则客户端将发送PING请求以使会话保持活动状态。此PING请求不仅允许ZooKeeper服务器知道客户端仍处于活动状态,而且还允许客户端验证其与ZooKeeper服务器的连接是否仍处于活动状态。PING的时间足够保守,以确保有合理的时间来检测死连接并重新连接到新服务器。

一旦成功建立(连接)与服务器的连接,基本上有两种情况,客户端lib生成connectionloss(c绑定中的结果代码,Java中的异常 - 请参阅API文档以获取绑定特定详细信息)执行异步操作,并执行以下操作之一:

  1. 应用程序在不再存活/有效的会话上调用操作
  2. 当存在对该服务器的挂起操作时,ZooKeeper客户端与服务器断开连接,即存在挂起的异步调用。

在3.2.0中添加 - SessionMovedException。有一个内部异常通常被称为SessionMovedException的客户端看不到。发生此异常是因为在已在其他服务器上重新建立的会话的连接上收到了请求。此错误的正常原因是客户端向服务器发送请求,但网络数据包延迟,因此客户端超时并连接到新服务器。当延迟数据包到达第一个服务器时,旧服务器检测到会话已移动,并关闭客户端连接。客户端通常不会看到此错误,因为它们不会从这些旧连接中读取。(旧连接通常是关闭的。)可以看到这种情况的一种情况是两个客户端尝试使用保存的会话ID和密码重新建立相同的连接。

更新服务器列表。我们允许客户端通过提供以逗号分隔的主机:端口对列表来更新连接字符串,每个端口对应一个ZooKeeper服务器。该函数调用概率负载平衡算法,该算法可能导致客户端与其当前主机断开连接,目标是在新列表中实现每个服务器的预期统一连接数。如果客户端连接的当前主机不在新列表中,则此调用将始终导致连接被删除。否则,决定是基于服务器数量是增加还是减少以及增加多少。

例如,如果先前的连接字符串包含3个主机,并且现在该列表包含这3个主机和2个以上的主机,则连接到3个主机中的每个主机的40%的客户端将移动到其中一个新主机以平衡负载。该算法将使客户端以概率0.4丢弃与其连接的当前主机的连接,并且在这种情况下使客户端连接到随机选择的2个新主机之一。

另一个例子 - 假设我们有5个主机,现在更新列表以删除2个主机,连接到3个剩余主机的客户端将保持连接,而连接到2个被删除主机的所有客户端将需要移动到其中一个主机3个主机,随机选择。如果连接断开,客户端将转到特殊模式,在该模式下,他选择使用概率算法连接的新服务器,而不仅仅是循环。

在第一个示例中,每个客户端决定以0.4的概率断开连接但是一旦做出决定,它将尝试连接到随机的新服务器,并且只有当它无法连接到任何新服务器时才会尝试连接到旧服务器那些。找到服务器或尝试新列表中的所有服务器并且无法连接后,客户端将返回到正常操作模式,从connectString中选择任意服务器并尝试连接到该服务器。如果失败,将继续在循环中尝试不同的随机服务器。(参见上面用于初步选择服务器的算法)

ZooKeeper手表

ZooKeeper中的所有读取操作 - getData()getChildren()exists() - 都可以选择将监视设置为副作用。这是ZooKeeper对手表的定义:手表事件是一次性触发,发送到设置手表的客户端,当手表设置的数据发生变化时会发生这种情况。在这个手表定义中需要考虑三个要点:

  • 一次性触发器 当数据发生变化时,将向客户端发送一个监视事件。例如,如果客户端执行getData(“/ znode1”,true),稍后更改或删除/ znode1的数据,则客户端将获得/ znode1的监视事件。如果/ znode1再次更改,则除非客户端已执行另一次设置新监视的读取,否则不会发送监视事件。
  • 发送到客户端 这意味着事件正在到达客户端的路上,但在更改操作的成功返回代码到达发起更改的客户端之前可能无法到达客户端。手表以异步方式发送给观察者。ZooKeeper提供了一个订购保证:客户端在第一次看到监视事件之前,永远不会看到设置监视的更改。网络延迟或其他因素可能导致不同的客户端在不同时间看到更新的手表和返回代码。关键点在于,不同客户看到的所有内容都会有一致的顺序。
  • 手表设置的数据 这指的是节点可以改变的不同方式。将ZooKeeper视为维护两个手表列表是有帮助的:数据手表和儿童手表。getData()和exists()设置数据监视。getChildren()设置子监视。或者,可以根据返回的数据类型来考虑设置手表。getData()和exists()返回有关节点数据的信息,而getChildren()返回子节点列表。因此,setData()将触发正在设置的znode的数据监视(假设该集成功)。成功的create()将触发正在创建的znode的数据监视和子监视父znode。成功的delete()将触发数据监视和子监视(因为没有更多子项),因为要删除的znode以及父znode的子监视。

手表在本地维护客户端所连接的ZooKeeper服务器。这使得手表可以轻量化以进行设置,维护和调度。当客户端连接到新服务器时,将针对任何会话事件触发监视。从服务器断开连接时不会收到手表。当客户重新连接时,任何以前注册的手表将被重新注册并在需要时触发。一般来说,这一切都是透明的。有一种情况可能会遗漏手表:如果在断开连接时创建并删除了znode,则会遗漏手表中是否存在尚未创建的znode。

手表的语义

我们可以使用读取ZooKeeper状态的三个调用来设置监视:exists,getData和getChildren。以下列表详细说明了手表可以触发的事件以及启用它们的调用:

  • 创建的事件: 通过调用存在启用。
  • 已删除事件: 通过调用exists,getData和getChildren启用。
  • 更改事件: 通过调用exists和getData启用。
  • 子事件: 通过调用getChildren启用。

删除手表

我们可以通过调用removeWatches删除在znode上注册的手表。此外,通过将local标志设置为true,即使没有服务器连接,ZooKeeper客户端也可以在本地删除监视。以下列表详细说明了成功删除手表后将触发的事件。

  • Child Remove事件: 添加了对getChildren的调用的Watcher。
  • 数据删除事件: 添加了对exists或getData的调用的Watcher。

ZooKeeper对手表的保证

关于手表,ZooKeeper保留以下保证:

  • 手表是针对其他事件,其他手表和异步回复而订购的。ZooKeeper客户端库确保按顺序分派所有内容。

  • 在查看与该znode对应的新数据之前,客户端将看到它正在观看的znode的监视事件。

  • ZooKeeper的监视事件的顺序对应于ZooKeeper服务所看到的更新顺序。

关于手表的事情

  • 手表是一次触发; 如果您收到观看活动并希望收到有关未来更改的通知,则必须设置另一个观看。

  • 因为手表是一次性触发器,并且在获取事件和发送新请求以获取手表之间存在延迟,因此您无法可靠地看到ZooKeeper中的节点发生的每个更改。准备好处理znode在获取事件和再次设置监视之间多次更改的情况。(你可能不在乎,但至少意识到它可能会发生。)

  • 对于给定通知,只会触发一次监视对象或函数/上下文对。例如,如果为同一个文件的exists和getData调用注册了相同的watch对象,然后删除该文件,则只能使用该文件的删除通知调用watch对象一次。

  • 当您断开与服务器的连接时(例如,当服务器发生故障时),在重新建立连接之前,您将不会获得任何监视。因此,会话事件将发送给所有优秀的手表处理程序。使用会话事件进入安全模式:断开连接时不会接收事件,因此您的过程应该在该模式下保守地执行。

使用ACL的ZooKeeper访问控制

ZooKeeper使用ACL来控制对其znode(ZooKeeper数据树的数据节点)的访问。ACL实现与UNIX文件访问权限非常相似:它使用权限位来允许/禁止针对节点的各种操作以及位应用的范围。与标准UNIX权限不同,ZooKeeper节点不受用户(文件所有者),组和世界(其他)的三个标准范围的限制。ZooKeeper没有znode所有者的概念。相反,ACL指定与这些ID关联的ID和权限集。

另请注意,ACL仅适用于特定的znode。特别是它不适用于儿童。例如,如果 / app只能通过ip读取:172.16.16.1和 / app / status是世界可读的,那么任何人都可以读取/ app / status ; ACL不是递归的。

ZooKeeper支持可插拔的身份验证方案。使用表单scheme:expression指定ID ,其中scheme是id对应的认证方案。有效表达式集由方案定义。例如,ip:172.16.16.1是 使用ip方案的地址为172.16.16.1的主机的id ,而digest:bob:password 是使用摘要方案的名称为bob的用户的id 。

当客户端连接到ZooKeeper并对其自身进行身份验证时,ZooKeeper会将与客户端对应的所有ID与客户端连接相关联。当客户端尝试访问节点时,将根据znode的ACL检查这些ID。ACL由(scheme:expression,perms)对组成表达式的格式特定于该方案。例如,该对(ip:19.22.0.0/16,READ) 为任何IP地址以19.22开头的客户端提供READ权限。

ACL权限

ZooKeeper支持以下权限:

  • CREATE:您可以创建子节点
  • READ:您可以从节点获取数据并列出其子节点。
  • WRITE:您可以为节点设置数据
  • 删除:您可以删除子节点
  • ADMIN:您可以设置权限

创建 和删除权限都被打破了的了细粒度的访问控制权限。对于案件CREATE 和DELETE有以下几种:

您希望A能够在ZooKeeper节点上执行集合,但无法创建 或删除子节点。

CREATE without DELETE:客户端通过在父目录中创建ZooKeeper节点来创建请求。您希望所有客户端都能够添加,但只有请求处理器才能删除。(这有点像文件的APPEND权限。)

此外,由于ZooKeeper没有文件所有者的概念,因此存在ADMIN权限。在某种意义上,ADMIN权限将实体指定为所有者。ZooKeeper不支持LOOKUP权限(对目录执行权限位以允许您进行LOOKUP,即使您无法列出目录)。每个人都隐含地拥有LOOKUP权限。这允许您统计节点,但仅此而已。(问题是,如果要在不存在的节点上调用zoo_exists(),则无权检查。)

内置ACL方案

ZooKeeeper具有以下内置方案:

  • 世界有一个id,任何人,代表任何人。
  • auth是一种特殊方案,它忽略任何提供的表达式,而是使用当前用户,凭据和方案。任何表达(无论用户喜欢用SASL认证或用户:密码等与DIGEST认证)提供由动物园管理员服务器持续的ACL时忽略。但是,仍必须在ACL中提供表达式,因为ACL必须与表单scheme:expression:perms匹配。提供此方案是为了方便,因为它是用户创建znode然后将该znode的访问权限仅限于该用户的常见用例。如果没有经过身份验证的用户,则使用auth方案设置ACL将失败。
  • 摘要使用username:password字符串生成MD5哈希,然后将其用作ACL ID标识。通过以明文形式发送用户名:密码来完成身份验证。在ACL中使用时,表达式将是用户名:base64 编码的SHA1 密码摘要
  • ip使用客户端主机IP作为ACL ID标识。的ACL表达的形式的地址/位,其中最显著 的地址是针对最显著匹配的客户端主机的IP。
  • x509使用客户端X500 Principal作为ACL ID标识。ACL表达式是客户端的确切X500主体名称。使用安全端口时,会自动对客户端进行身份验证,并设置x509方案的身份验证信息。

ZooKeeper C客户端API

ZooKeeper C库提供以下常量:

  • const int ZOO_PERM_READ; //可以读取节点的值并列出其子节点
  • const int ZOO_PERM_WRITE; //可以设置节点的值
  • const int ZOO_PERM_CREATE; //可以创建孩子
  • const int ZOO_PERM_DELETE; //可以删除子项
  • const int ZOO_PERM_ADMIN; //可以执行set_acl()
  • const int ZOO_PERM_ALL; //所有上述标志一起OR

以下是标准ACL ID:

  • struct Id ZOO_ANYONE_ID_UNSAFE; //( '世界', '人')
  • struct Id ZOO_AUTH_IDS; //('auth','')

ZOO_AUTH_IDS空标识字符串应解释为“创建者的身份”。

ZooKeeper客户端带有三个标准ACL:

  • struct ACL_vector ZOO_OPEN_ACL_UNSAFE; //(ZOO_PERM_ALL,ZOO_ANYONE_ID_UNSAFE)
  • struct ACL_vector ZOO_READ_ACL_UNSAFE; //(ZOO_PERM_READ,ZOO_ANYONE_ID_UNSAFE)
  • struct ACL_vector ZOO_CREATOR_ALL_ACL; //(ZOO_PERM_ALL,ZOO_AUTH_IDS)

ZOO_OPEN_ACL_UNSAFE对所有ACL完全免费打开:任何应用程序都可以在节点上执行任何操作,并可以创建,列出和删除其子节点。ZOO_READ_ACL_UNSAFE是任何应用程序的只读访问权限。CREATE_ALL_ACL向节点的创建者授予所有权限。创建者必须先由服务器进行身份验证(例如,使用“ 摘要 ”方案),然后才能使用此ACL创建节点。

以下ZooKeeper操作处理ACL:

  • int zoo_add_auth (zhandle_t * zh,const char * scheme,const char * cert,int certLen,void_completion_t completion,const void * data);

应用程序使用zoo_add_auth函数向服务器验证自身。如果应用程序想要使用不同的方案和/或身份进行身份验证,则可以多次调用该函数。

  • int zoo_create (zhandle_t * zh,const char * path,const char * value,int valuelen,const struct ACL_vector * acl,int flags,char * realpath,int max_realpath_len);

zoo_create(...)操作创建一个新节点。acl参数是与节点关联的ACL列表。父节点必须设置CREATE权限位。

  • int zoo_get_acl (zhandle_t * zh,const char * path,struct ACL_vector * acl,struct Stat * stat);

此操作返回节点的ACL信息。

  • int zoo_set_acl (zhandle_t * zh,const char * path,int version,const struct ACL_vector * acl);

此函数用新的ACL替换节点的ACL列表。该节点必须具有ADMIN权限集。

下面是一个示例代码,它使用上述API使用“ foo ”方案对自身进行身份验证,并使用create-only权限创建一个短暂的节点“/ xyz”。

######注意

这是一个非常简单的示例,旨在说明如何与ZooKeeper ACL进行交互。有关 C客户端实现的示例,请参阅... / trunk / zookeeper-client / zookeeper-client-c / src / cli.c

#include <string.h>
#include <errno.h>#include "zookeeper.h"static zhandle_t *zh;/*** In this example this method gets the cert for your*   environment -- you must provide*/
char *foo_get_cert_once(char* id) { return 0; }/** Watcher function -- empty for this example, not something you should* do in real code */
void watcher(zhandle_t *zzh, int type, int state, const char *path,void *watcherCtx) {}int main(int argc, char argv) {char buffer[512];char p[2048];char *cert=0;char appId[64];strcpy(appId, "example.foo_test");cert = foo_get_cert_once(appId);if(cert!=0) {fprintf(stderr,"Certificate for appid [%s] is [%s]\n",appId,cert);strncpy(p,cert, sizeof(p)-1);free(cert);} else {fprintf(stderr, "Certificate for appid [%s] not found\n",appId);strcpy(p, "dummy");}zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);zh = zookeeper_init("localhost:3181", watcher, 10000, 0, 0, 0);if (!zh) {return errno;}if(zoo_add_auth(zh,"foo",p,strlen(p),0,0)!=ZOK)return 2;struct ACL CREATE_ONLY_ACL[] = {{ZOO_PERM_CREATE, ZOO_AUTH_IDS}};struct ACL_vector CREATE_ONLY = {1, CREATE_ONLY_ACL};int rc = zoo_create(zh,"/xyz","value", 5, &CREATE_ONLY, ZOO_EPHEMERAL,buffer, sizeof(buffer)-1);/** this operation will fail with a ZNOAUTH error */int buflen= sizeof(buffer);struct Stat stat;rc = zoo_get(zh, "/xyz", 0, buffer, &buflen, &stat);if (rc) {fprintf(stderr, "Error %d for %s\n", rc, __LINE__);}zookeeper_close(zh);return 0;
}

可插入ZooKeeper身份验证

ZooKeeper在各种不同的环境中运行,具有各种不同的身份验证方案,因此它具有完全可插入的身份验证框架。甚至内置的身份验证方案也使用可插入的身份验证框架。

要了解身份验证框架的工作原理,首先必须了解两个主要的身份验证操作。框架首先必须验证客户端。这通常在客户端连接到服务器时完成,包括验证从客户端发送或收集的有关客户端的信息并将其与连接相关联。框架处理的第二个操作是在ACL中查找与客户端对应的条目。ACL条目是< idspec,permissions >对。该idspec可以是针对与连接相关联的认证信息的简单字符串匹配,或者它可以是针对该信息评估的表达式。由认证插件的实现来进行匹配。以下是身份验证插件必须实现的接口:

public interface AuthenticationProvider {String getScheme();KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte authData[]);boolean isValid(String id);boolean matches(String id, String aclExpr);boolean isAuthenticated();
}

第一个方法getScheme返回标识插件的字符串。因为我们支持多种身份验证方法,所以身份验证凭据或idspec将始终以scheme:为前缀。ZooKeeper服务器使用身份验证插件返回的方案来确定该方案适用的ID。

当客户端发送与连接关联的身份验证信息时,将调用handleAuthentication。客户端指定信息对应的方案。ZooKeeper服务器将信息传递给身份验证插件,其 getScheme与客户端传递的方案匹配。handleAuthentication的实现者通常会在确定信息不正确时返回错误,或者它会使用cnxn.getAuthInfo()。add(new Id(getScheme(),data))将信息与连接相关联。

身份验证插件涉及设置和使用ACL。为znode设置ACL时,ZooKeeper服务器会将条目的id部分传递给isValid(String id)方法。由插件来验证id是否具有正确的形式。例如,ip:172.16.0.0/16 是有效的ID,但ip:host.com不是。如果新ACL包含“auth”条目,则使用isAuthenticated查看是否应将与该连接关联的此方案的身份验证信息添加到ACL。一些方案不应包括在auth中。例如,如果指定了auth,则不将客户端的IP地址视为应添加到ACL的ID。

检查ACL时 ZooKeeper会调用匹配项(String id,String aclExpr)。需要将客户端的认证信息与相关的ACL条目进行匹配。为了找到适用于客户端的条目,ZooKeeper服务器将找到每个条目的方案,如果该方案有来自该客户端的认证信息, 将调用匹配(String id,String aclExpr),并将id设置为认证先前通过handleAuthentication和 aclExpr添加到连接的信息设置为ACL条目的id。身份验证插件使用自己的逻辑和匹配方案来确定aclExpr中是否包含id

有两个内置的身份验证插件:ip和 digest。其他插件可以使用系统属性添加。在启动时,ZooKeeper服务器将查找以“zookeeper.authProvider”开头的系统属性。并将这些属性的值解释为身份验证插件的类名。可以使用-Dzookeeeper.authProvider.X = com.f.MyAuth设置这些属性, 或在服务器配置文件中添加以下条目:

authProvider.1=com.f.MyAuth
authProvider.2=com.f.MyAuth2

应注意确保物业上的后缀是唯一的。如果有重复项,例如-Dzookeeeper.authProvider.X = com.f.MyAuth -Dzookeeper.authProvider.X = com.f.MyAuth2,则只使用一个。此外,所有服务器都必须定义相同的插件,否则使用插件提供的身份验证方案的客户端将无法连接到某些服务器。

在3.6.0中添加:可插拔身份验证的备用抽象。它提供了额外的参数。

public abstract class ServerAuthenticationProvider implements AuthenticationProvider {public abstract KeeperException.Code handleAuthentication(ServerObjs serverObjs, byte authData[]);public abstract boolean matches(ServerObjs serverObjs, MatchValues matchValues);
}

您可以扩展ServerAuthenticationProvider而不是实现AuthenticationProvider。然后,handleAuthentication()和matches()方法将接收其他参数(通过ServerObjs和MatchValues)。

  • ZooKeeperServer ZooKeeperServer实例
  • ServerCnxn 当前连接
  • path 正在操作的ZNode路径(如果未使用,则为null)
  • perm 操作值或0
  • setAcls 正在操作setAcl()方法时,正在设置的ACL列表

一致性保证

ZooKeeper是一种高性能,可扩展的服务。尽管读取速度比写入速度快,但读取和写入操作都设计得很快。这样做的原因是,在读取的情况下,ZooKeeper可以提供较旧的数据,而这又归功于ZooKeeper的一致性保证:

  • 顺序一致性:客户端的更新将按发送顺序应用。

  • 原子性:更新成功或失败 - 没有部分结果。

  • 单系统映像:无论服务器连接到哪个服务器,客户端都将看到相同的服务视图。

  • 可靠性:一旦应用了更新,它将从那时起持续到客户端覆盖更新。这种保证有两个推论:

    1. 如果客户端获得成功的返回代码,则将应用更新。在某些故障(通信错误,超时等)上,客户端将不知道更新是否已应用。我们采取措施尽量减少故障,但只有成功的返回码才能保证。(这被称为Paxos中的单调性条件。)
    2. 客户端通过读取请求或成功更新看到的任何更新在从服务器故障中恢复时都不会回滚。
  • 及时性:系统的客户视图保证在一定时间内(大约几十秒)是最新的。在此范围内,客户端将看到系统更改,或者客户端将检测到服务中断。

使用这些一致性保证只需在ZooKeeper客户端(ZooKeeper不需要添加)就可以轻松构建更高级别的功能,例如领导者选举,障碍,队列和读/写可撤销锁。有关 详细信息,请参阅配方和解决方案。

######注意

有时开发人员会错误地假设ZooKeeper实际上没有做出其他保证。这是:

  • 同时一致的跨客户端视图*:ZooKeeper不保证在每个实例中,两个不同的客户端将具有相同的ZooKeeper数据视图。由于网络延迟等因素,一个客户端可能会在另一个客户端收到更改通知之前执行更新。考虑两个客户端A和B的场景。如果客户端A将znode / a的值从0设置为1,然后告诉客户端B读取/ a,则客户端B可以读取旧值0,具体取决于哪个服务器它连接到。如果客户端A和客户端B读取相同值很重要,则客户端B应该调用sync()ZooKeeper API方法执行读取之前的方法。因此,ZooKeeper本身并不保证所有服务器上的更改同步发生,但ZooKeeper原语可用于构建更高级别的函数,以提供有用的客户端同步。(有关更多信息,请参阅ZooKeeper Recipes。 [tbd:..])。

绑定

ZooKeeper客户端库有两种语言:Java和C.以下部分描述了这些。

Java绑定

组成ZooKeeper Java绑定有两个包: org.apache.zookeeperorg.apache.zookeeper.data。构成ZooKeeper的其余软件包在内部使用或者是服务器实现的一部分。所述org.apache.zookeeper.data包是由被简单地用作容器生成的类。

ZooKeeper Java客户端使用的主类是ZooKeeper类。它的两个构造函数只有一个可选的会话ID和密码。ZooKeeper支持跨进程实例的会话恢复。Java程序可以将其会话ID和密码保存到稳定存储,重新启动并恢复早期程序实例使用的会话。

创建ZooKeeper对象时,也会创建两个线程:IO线程和事件线程。所有IO都发生在IO线程上(使用Java NIO)。所有事件回调都发生在事件线程上。会话维护(例如重新连接到ZooKeeper服务器和维护心跳)在IO线程上完成。同步方法的响应也在IO线程中处理。异步方法和监视事件的所有响应都在事件线程上处理。有一些事情要注意这个设计的结果:

  • 异步调用和观察者回调的所有完成将按顺序进行,一次一个。调用者可以进行他们希望的任何处理,但在此期间不会处理任何其他回调。
  • 回调不会阻止IO线程的处理或同步调用的处理。
  • 同步调用可能无法按正确顺序返回。例如,假设客户端执行以下处理:发出节点/ a的异步读取,并将 watch设置为true,然后在读取的完成回调中执行/ a的同步读取。(也许不是很好的做法,但也不是非法的,它只是一个简单的例子。)注意,如果异步读取和同步读取之间有/ a的更改,客户端库将收到说/ a的监视事件在同步读取的响应之前更改,但由于完成回调阻塞事件队列,同步读取将返回新值/ a 在处理监视事件之前。

最后,与关闭相关的规则很简单:一旦ZooKeeper对象关闭或收到致命事件(SESSION_EXPIRED和AUTH_FAILED),ZooKeeper对象就变为无效。在结束时,两个线程关闭,对zookeeper句柄的任何进一步访问都是未定义的行为,应该避免。

客户端配置参数

以下列表包含Java客户端的配置属性。您可以使用Java系统属性设置任何这些属性。有关服务器属性,请查看以下参考 服务器配置部分。

  • zookeeper.sasl.client:将值设置为false以禁用SASL身份验证。默认为true

  • zookeeper.sasl.clientconfig:指定JAAS登录文件中的上下文密钥。默认为“客户”。

  • zookeeper.sasl.client.username:传统上,主体分为三个部分:主要部​​分,实例和领域。典型Kerberos V5主体的格式是primary / instance @ REALM。zookeeper.sasl.client.username指定服务器主体的主要部分。默认为“zookeeper”。实例部分源自服务器IP。最后,服务器的主体是username / IP @ realm,其中username是zookeeper.sasl.client.username的值,IP是服务器IP,realm是zookeeper.server.realm的值。

  • zookeeper.server.realm:服务器主体的Realm部分。默认情况下,它是客户端主要领域。

  • zookeeper.disableAutoWatchReset:此开关控制是否启用自动手表重置。默认情况下,客户端在会话重新连接期间自动重置监视,此选项允许客户端通过将zookeeper.disableAutoWatchReset设置为true来关闭此行为。

  • zookeeper.client.secure:如果要连接到服务器安全客户端端口,则需要 在客户端上将此属性设置为 true。这将使用具有指定凭据的SSL连接到服务器。请注意,它需要Netty客户端。

  • zookeeper.clientCnxnSocket:指定要使用的ClientCnxnSocket。可能的值是org.apache.zookeeper.ClientCnxnSocketNIO 和 org.apache.zookeeper.ClientCnxnSocketNetty 。默认为org.apache.zookeeper.ClientCnxnSocketNIO 。如果要连接到服务器的安全客户端端口,则需要 在客户端上将此属性设置为 org.apache.zookeeper.ClientCnxnSocketNetty

  • zookeeper.ssl.keyStore.location和zookeeper.ssl.keyStore.password:指定包含要用于SSL连接的本地凭据的JKS的文件路径,以及用于解锁文件的密码。

  • zookeeper.ssl.trustStore.location和zookeeper.ssl.trustStore.password:指定包含要用于SSL连接的远程凭据的JKS的文件路径,以及用于解锁文件的密码。

  • jute.maxbuffer:它指定来自服务器的传入数据的最大大小。默认值为4194304字节,或仅4 MB。这真是一个健全检查。ZooKeeper服务器用于存储和发送千字节数据。如果传入数据长度超过此值,则引发IOException。

  • zookeeper.kinit:指定kinit二进制文件的路径。默认为“/ usr / bin / kinit”。

C绑定

C绑定具有单线程和多线程库。多线程库最容易使用,与Java API最相似。该库将创建一个IO线程和一个事件调度线程,用于处理连接维护和回调。单线程库允许ZooKeeper通过公开多线程库中使用的事件循环在事件驱动的应用程序中使用。

该软件包包括两个共享库:zookeeper_st和zookeeper_mt。前者仅提供异步API和回调以集成到应用程序的事件循环中。这个库存在的唯一原因是支持平台是 pthread库不可用或不稳定(即FreeBSD 4.x)。在所有其他情况下,应用程序开发人员应该与zookeeper_mt链接,因为它包括对Sync和Async API的支持。

安装

如果您从Apache存储库的签出中构建客户端,请按照下面列出的步骤进行操作。如果您是从从apache下载的项目源包构建的,请跳到步骤3

  1. 运行ant compile_jute在的ZooKeeper顶级目录(... /主干)。这将在... / trunk / zookeeper-client / zookeeper-client-c下创建一个名为“generated”的目录 。
  2. 将目录更改为* ... / trunk / zookeeper-client / zookeeper-client-c *并运行autoreconf -if到bootstrap autoconfautomakelibtool。确保安装了autoconf版本2.59或更高版本。跳到第4步。
  3. 如果要从项目源包构建,请解压缩/解压缩源tarball并cd到* zookeeper-xxx / zookeeper-client / zookeeper-client-c *目录。
  4. 运行./configure <your-options>以生成makefile。以下是configure实用程序支持的一些选项,这些选项在此步骤中非常有用:
  • --enable-debug 启用优化并启用调试信息编译器选项。(默认情况下禁用。)
  • --without-syncapi 禁用Sync API支持; 不会构建zookeeper_mt库。(默认启用。)
  • --disable-static 不要构建静态库。(默认启用。)
  • --disable-shared 不要构建共享库。(默认情况下启用。)######注意

有关运行configure的一般信息,请参阅INSTALL 。

  1. 运行makemake install构建库并安装它们。
  2. 要为ZooKeeper API生成doxygen文档,请运行 make doxygen-doc。所有文档都将放在名为docs的新子文件夹中。默认情况下,此命令仅生成HTML。有关其他文档格式的信息,请运行./configure --help

建立自己的C客户端

为了能够在您的应用程序中使用ZooKeeper C API,您必须记住

  1. 包含ZooKeeper标头: #include <zookeeper/zookeeper.h>
  2. 如果要构建多线程客户端,请使用 -DTHREADED编译器标志进行编译以启用库的多线程版本,然后针对zookeeper_mt库进行链接 。如果要构建单线程客户端,请不要编译-DTHREADED,并确保链接到_zookeeper_st_library。

######注意

有关 C客户端实现的示例,请参阅... / trunk / zookeeper-client / zookeeper-client-c / src / cli.c

构建块:ZooKeeper操作指南

本节将介绍开发人员可以对ZooKeeper服务器执行的所有操作。它是比本手册中早期概念章节更低级别的信息,但比ZooKeeper API Reference更高级别。它涵盖了以下主题:

  • 连接到ZooKeeper

处理错误

Java和C客户端绑定都可能报告错误。Java客户端绑定通过抛出KeeperException来实现,在异常上调用code()将返回特定的错误代码。C客户端绑定返回枚举ZOO_ERRORS中定义的错误代码。API回调指示两种语言绑定的结果代码。有关可能的错误及其含义的完整详细信息,请参阅API文档(Java的javadoc,C的doxygen)。

连接到ZooKeeper

在开始之前,您必须设置一个正在运行的Zookeeper服务器,以便我们可以开始开发客户端。对于C客户端绑定,我们将使用多线程库(zookeeper_mt)和一个用C编写的简单示例。为了与Zookeeper服务器建立连接,我们使用带有以下签名的C API - zookeeper_init

int zookeeper_init(const char *host, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags);
  • host:连接到zookeeper服务器的字符串,格式为host:port。如果有多个服务器,请在指定host:port对后使用逗号作为分隔符。例如:“127.0.0.1:2181,127.0.0.1:3001,127.0.0.1:3002”

  • fn:Watcher函数用于在触发通知时处理事件。

  • recv_timeout:会话到期时间(以毫秒为单位)。

  • clientid:我们可以为新会话指定0。如果会话之前已经建立,我们可以提供该客户端ID,它将重新连接到之前的会话。

  • context:可以与zkhandle_t处理程序关联的Context对象。如果未使用,我们可以将其设置为0。

  • flags:在启动时,我们可以将它保留为0。

我们将演示在成功连接后输出“Connected to Zookeeper”的客户端或否则输出错误消息。我们调用以下代码zkClient.cc

#include <stdio.h>
#include <zookeeper/zookeeper.h>
#include <errno.h>
using namespace std;// Keeping track of the connection state
static int connected = 0;
static int expired   = 0;// *zkHandler handles the connection with Zookeeper
static zhandle_t *zkHandler;// watcher function would process events
void watcher(zhandle_t *zkH, int type, int state, const char *path, void *watcherCtx)
{if (type == ZOO_SESSION_EVENT) {// state refers to states of zookeeper connection.// To keep it simple, we would demonstrate these 3: ZOO_EXPIRED_SESSION_STATE, ZOO_CONNECTED_STATE, ZOO_NOTCONNECTED_STATE// If you are using ACL, you should be aware of an authentication failure state - ZOO_AUTH_FAILED_STATEif (state == ZOO_CONNECTED_STATE) {connected = 1;} else if (state == ZOO_NOTCONNECTED_STATE ) {connected = 0;} else if (state == ZOO_EXPIRED_SESSION_STATE) {expired = 1;connected = 0;zookeeper_close(zkH);}}
}int main(){zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);// zookeeper_init returns the handler upon a successful connection, null otherwisezkHandler = zookeeper_init("localhost:2181", watcher, 10000, 0, 0, 0);if (!zkHandler) {return errno;}else{printf("Connection established with Zookeeper. \n");}// Close Zookeeper connectionzookeeper_close(zkHandler);return 0;
}

使用前面提到的多线程库编译代码。

> g++ -Iinclude/ zkClient.cpp -lzookeeper_mt -o Client

运行客户端。

> ./Client

从输出中,如果连接成功,您应该看到“已连接到Zookeeper”以及Zookeeper的DEBUG消息。

阅读操作

写操作

处理手表

错误的ZooKeeper操作

程序结构,简单示例

[TBD]

陷阱:常见问题和故障排除

所以现在你知道ZooKeeper了。它快速,简单,你的应用程序工作,但等待......出了点问题。以下是ZooKeeper用户陷入的一些陷阱:

  1. 如果您使用手表,则必须查找已连接的手表事件。当ZooKeeper客户端与服务器断开连接时,在重新连接之前,您不会收到更改通知。如果您正在观察znode是否存在,如果在断开连接时创建并删除了znode,您将错过该事件。
  2. 您必须测试ZooKeeper服务器故障。只要大多数服务器处于活动状态,ZooKeeper服务就可以在故障中存活。要问的问题是:您的申请可以处理吗?在现实世界中,客户端与ZooKeeper的连接可能会中断。(ZooKeeper服务器故障和网络分区是连接丢失的常见原因。)ZooKeeper客户端库负责恢复连接并让您知道发生了什么,但您必须确保恢复状态和任何未通过的未完成请求。找出你是否在测试实验室中得到了它,而不是在生产中 - 使用由几个服务器组成的ZooKeeper服务进行测试并让它们重新启动。
  3. 客户端使用的ZooKeeper服务器列表必须与每个ZooKeeper服务器所拥有的ZooKeeper服务器列表相匹配。如果客户端列表是ZooKeeper服务器真实列表的子集,那么事情可以起作用,尽管不是最佳的,但如果客户端列出不在ZooKeeper集群中的ZooKeeper服务器则不行。
  4. 放置该事务日志的位置要小心。ZooKeeper中性能最关键的部分是事务日志。ZooKeeper必须在返回响应之前将事务同步到媒体。专用的事务日志设备是始终如一的良好性能的关键。将日志置于繁忙的设备上会对性能产生负面影响。如果您只有一个存储设备,请将跟踪文件放在NFS上并增加snapshotCount; 它并没有消除问题,但它可以减轻它。
  5. 正确设置Java最大堆大小。避免交换非常重要 不必要地转向磁盘几乎肯定会令您无法接受地降低性能。请记住,在ZooKeeper中,所有内容都是有序的,因此如果一个请求命中磁盘,则所有其他排队请求都会命中磁盘。要避免交换,请尝试将堆大小设置为您拥有的物理内存量减去操作系统和缓存所需的数量。确定配置的最佳堆大小的最佳方法是运行负载测试。如果由于某种原因你不能,在你的估计中保守,并选择一个远低于导致你的机器交换的限制的数字。例如,在4G机器上,3G堆是一个保守的估计开始。

其他信息的链接

在正式文档之外,ZooKeeper开发人员还有其他几个信息来源。

  • ZooKeeper白皮书[tbd:find url]:Yahoo!对ZooKeeper设计和性能的最终讨论 研究

  • API参考[tbd:find url]:ZooKeeper API的完整参考

  • 2008年Hadoup峰会上的ZooKeeper讲座:雅虎的Benjamin Reed对ZooKeeper的视频介绍 研究

  • 屏障和队列教程:Flavio Junqueira的优秀Java教程,使用ZooKeeper实现简单的障碍和生产者 - 消费者队列。

  • ZooKeeper - 可靠,可扩展的分布式协调系统:Todd Hoff的一篇文章(07/15/2008)

  • ZooKeeper Recipes:使用ZooKeeper实现各种同步解决方案的伪级讨论:事件句柄,队列,锁和两阶段提交。

  • [tbd]:任何人都能想到的其他好消息来源......

转载来源:https://github.com/apache/zookeeper/blob/master/zookeeper-docs/src/main/resources/markdown/zookeeperProgrammers.md

ZooKeeper程序员指南--使用ZooKeeper开发分布式应用程序相关推荐

  1. python程序猿_python程序员指南 pdf下载

    python程序员指南 pdf是一本专为对python编程感兴趣的朋友准备的指导图书,作从最基本的基础知识到繁琐的运用,都进行的详细的解答,是你自学的最好教程了,感兴趣欢迎下载学习! python程序 ...

  2. ZooKeeper官方文档学习笔记03-程序员指南

    害,终究是我高估了自己,这个也太多了.不过里面有些思想,让我感觉似曾相识,比如他的session就有点像HTTPS,然后session的管理有点像人的管理 使用 ZooKeeper 开发分布式应用程序 ...

  3. ZooKeeper官方文档学习笔记03-程序员指南03

    我的每一篇这种正经文章,都是我努力克制玩心的成果,我可太难了,和自己做斗争. ZooKeeper官方文档学习笔记04-程序员指南03 绑定 Java绑定 客户端配置参数 C绑定 陷阱: 常见问题及故障 ...

  4. ZooKeeper官方文档学习笔记03-程序员指南02

    这个太多了 我总是坚持不下来,还是分开写吧,这样更有成就感 程序员指南02 使用ACL的ZooKeeper访问控制 permission schema Zookeeper的C语言client API ...

  5. BAT 程序员们常用的开发工具

    阿里篇 一.Java 线上诊断工具 Arthas Arthas 是阿里巴巴 2018 年 9 月开源的一款 Java 线上诊断工具. 使用场景: 这个类从哪个 jar 包加载的?为什么会报各种类相关的 ...

  6. BAT程序员们常用的开发神器

    BAT程序员们常用的开发神器 黄小斜 今天 作者丨InfoQ编辑部 工欲善其事必先利其器,一个优秀的程序员除了代码写得好,善于利用各种开发工具同样可以事半功倍.以 BAT 为代表的各大厂程序员们在平时 ...

  7. 收藏!!BAT 程序员们常用的开发工具

    阅读本文大概需要 15 分钟. 作者:infoQ来源:https://tinyurl.com/y4wohyqt 工欲善其事必先利其器,一个优秀的程序员除了代码写得好,善于利用各种开发工具同样可以事半功 ...

  8. Python最抢手、Go最有前途,7000位程序员揭秘2019软件开发现状

    作者 | 屠敏 整理 报告来源 | JetBrains 转载自 CSDN(ID:CSDNnews) 互联网的下半场,科技公司为面对更加严峻的竞争环境,越来越重视开源节流.而对于身处其中且撑起 IT 半 ...

  9. 3名程序员被抓!开发“万能钥匙”APP,撬走3个亿

    来自:程序员头条 报道 又有 3 名程序员被抓!开发"万能钥匙"APP,撬走 3 亿! 最近,据央视新闻报道,上海公安机关接到共享单车企业报案,随后破获了一起共享单车万能解锁 Ap ...

最新文章

  1. ubuntu18 安装python3.8.tgz
  2. C语言常用宏定义(#define)使用方法
  3. java读取InputStream输入流后输出String字符串
  4. .[转] 几米语录 生活永远不是童话
  5. 静态变量读取属性文件_一种通过变量插值读取属性的方法
  6. 超级连续的图片滚动特效
  7. 别人在加薪,你却在加班?快到这里和聪明的小伙伴一起充电吧!
  8. 【廖雪峰官方网站/Java教程】反射
  9. 推荐一个CodeProject上的SlideForm控件
  10. linux 用户操作相关命令
  11. 背诵华为hcia认证考试题库答案能过吗?华为认证等级是怎样的
  12. php影院影城源码,99影院源码 影视网站程序源码/附教程
  13. 淘宝开放平台深入浅出
  14. 取消Excel里面全部超级链接
  15. 苹果微信换行怎么打_微信朋友圈发长文被折叠成一行怎么破?附苹果安卓解决方案...
  16. 超实用硬盘数据恢复工具介绍!永久免费
  17. 2019中国脑科学与神经调控技术发展高峰论坛
  18. modbustcp测试工具怎么用_【转】年轻人不讲武德不仅白piao接口测试知识还白piao接口测试工具会员...
  19. 一个癌症病人的美国求医经历:活人死人如果都得不到尊重,病人也很难被尊重
  20. android-sdk-windows版本下载

热门文章

  1. HTML5: 全局属性
  2. Font Manager :字体管理及批量安装工具
  3. Magento教程 10:如何修改网站文字?
  4. Web开发者必备:Web应用检查清单
  5. linux 批量替换文件内容及查找某目录下所有包含某字符串的文件(批量修改文件内容)...
  6. Js Tween 实现
  7. linux压缩和解压缩命令汇总
  8. 安装图像标注工具Labelme并使用其制作自己的数据集
  9. 【设计模式】设计模式C++编程实现之策略模式(Strategy Pattern)
  10. 【C++基础学习】《C++ Primer》中的“有序型”