阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

ZooKeeper开发手册中文翻译

154次阅读
没有评论

共计 17609 个字符,预计需要花费 45 分钟才能阅读完成。

摘要 自己翻译的 ZooKeeper 官方文档,需要的人可以参考一下,有翻译错误还请指正。

目录

ZooKeeper 的数据模型
ZNodes
ZooKeeper 中的时间
ZooKeeper 状态结构(Stat Structure)
ZooKeeper Sessions
ZooKeeper Watches
使用 ACL 控制 ZooKeeper 访问权限
ACL 权限
内建 ACL 模式
ZooKeeper C client API
插件式 ZooKeeper 认证
一致性保证
编程接口
常见问题和故障

翻译:steve sun 博客地址:http://sundiontheway.github.io/blog

————————————– 分割线 ————————————–

ZooKeeper 集群配置 http://www.linuxidc.com/Linux/2013-06/86348.htm

使用 ZooKeeper 实现分布式共享锁 http://www.linuxidc.com/Linux/2013-06/85550.htm

分布式服务框架 ZooKeeper — 管理分布式环境中的数据 http://www.linuxidc.com/Linux/2013-06/85549.htm

ZooKeeper 集群环境搭建实践 http://www.linuxidc.com/Linux/2013-04/83562.htm

ZooKeeper 服务器集群环境配置实测 http://www.linuxidc.com/Linux/2013-04/83559.htm

ZooKeeper 集群安装 http://www.linuxidc.com/Linux/2012-10/72906.htm

————————————– 分割线 ————————————–

本文假设你已经具有一定分布式计算的基础知识。你将在第一部分看到以下内容:

  • ZooKeeper 数据模型

  • ZooKeeper Sessions

  • ZooKeeper Watches

  • 一致性保证(Consistency Guarantees)

接下来的 4 小节讲述了程序开发的实际应用:

  • 创建模块——ZooKeeper 操作指引

  • 编程语言接口

  • 简单示例演示程序的结构

  • 常见问题和故障

本文的附录中包含和 ZooKeeper 相关的有用信息。

ZooKeeper 的数据模型

ZooKeeper 有一个类似分布式文件系统的命名体系。区别在于 Zookeeper 每个一个节点或子节点都可以拥有数据。节点路径是一个由斜线分开的绝对路径,注意没有相对路径。只要满足下面要求的 unicode 字符都可以作为节点路径:

  • 空字符不能出现在路径名

  • 不能出现以下字符: \u0001 – \u0019 and \u007F – \u009F

  • 以下字符不允许使用: \ud800 -uF8FFF, \uFFF0-uFFFF, \uXFFFE – \uXFFFF (where X is a digit 1 – E), \uF0000 – \uFFFFF

  • 字符 ”.” 可以作为一个名字的一部分, 但是 ”.” 和 ”..” 不能单独作为相对路径使用, 以下用法都是无效的: “/a/b/./c” 或者 ”/a/b/../c”

  • “zookeeper” 为保留字符

ZNodes

ZooKeeper 树结构中的节点被称为 znode。各个 znode 维护着一组用来标记数据和访问权限发生变化的版本号。这些版本号组成的状态结构 具有时间戳。Zookeeper 使用版本号和时间戳来验证缓存状态,调整更新。每次 znode 中的数据发生变化,znode 的版本号增加。例如,每当一个客户端恢复数据时,它就接收这个版本的数据,而当一个客户端提交了更新或删除记 录,它必须同时提供这个 znode 当前正在发生变化的数据的版本。如果这个版本和目前真实的版本不匹配,则提交无效。__提示,在分布式程序中,一个字节点可以代表一个通用的主机,服务器,集群中的一员,客户端程序等。但是在 Zookeeper 中,znode 代表数据节 点,Servers 代表组成了 Zookeeper 服务的机器; quorum peers refer to the servers that  make up an ensemble; 客户端代表任何使用 ZooKeeper 服务的主机或程序。

znode 作为对程序开发来说最重要的信息,有几个特性需要特别关注下:

Watches 客户端可以在 znode 上设置 Watch。znode 发生的变化会触发 watch 然后清除 watch。当一个 watch 被触发,Zookeeper 给客户端发送一个通知。更多关于 watch 的内容请查看 ZooKeeper Watches 一节。

数据存取 命名空间中每个 znode 中的数据读写是原子操作。读操作读取 znode 中的所有数据位,写操作则替换所有数据。每个节点都有一个访问权限控制表(ACL)来标记谁可以做什么。zookeeper 不是设计成普通的数据库或大型对象存储的。它是用来管理 coordination data。coordination  data 包括配置文件、状态信息、rendezvous 等。这些数据结构的一个共同特点就是相对较小——以千字节为准。Zookeeper 的客户端和服务 会检查确保每个 znode 上的数据小于 1M,实际平均数据要远远小于 1M。大规模数据的操作会引发一些潜在的问题并且延长在网络和介质之间传输的时间。如果确实需要大型数据的存储,那么可以采用如 NFS 或 HDFS 之类的大型数据 存储系统,亦或是在 zookeeper 中存储指向存储位置的指针。

临时节点(Ephemeral Nodes) zookeeper 还有临时节点的概念,这些节点的生命周期依赖于创建它们的 session 是否活跃。session 结束时节点即被销毁。也由于这种特性,临时节点不允许有子节点。

序列节点——命名不唯一 当你创建节点的时候,你会需要 zookeeper 提供一组单调递增的计数来作为路径结尾。这个计数对父 znode 是唯一的。用%010d 的格式——用 0 来填充的 10 位数(计数如此命名是为了简单排序)。例如 ”0000000001″,注意计数器是有符号整型,超过表示范围会溢出。

ZooKeeper 中的时间

zookeeper 有很多记录时间的方式:

  • Zxid(ZooKeeper Transaction Id):zookeeper 每次发生改动都会增加 zxid,zxid 越大,发生的时间越靠后。

  • Version numbers:对 znode 的改动会增加版本号。版本号包括 version (znode 上数据的修改数), cversion (znode 的子节点的修改数), aversion (znode 上 ACL(权限)的修改数)。

  • Ticks : 多个 server 构成 zookeeper 服务时,各个 server 用 ticks 来标记如状态上报、连接超时等事件。ticks  time 还间接反映了 session 超时的最小值(两次 tick time);如果客户端请求的最小 session  timeout 低于这个最小值,服务端会通知客户端最小超时置为这个最小值。

  • Real time : 除了每次 znode 创建或改动时候将时间戳记录到状态结构中外,zookeeper 不使用时钟时间。

ZooKeeper 状态结构(Stat Structure)

存在于 znode 中的状态结构,由以下各个部分组成:

  • czxid – znode 创建产生的 zxid

  • mzxid – znode 最后一次修改的 zxid

  • ctime – znode 创建的时间的绝对毫秒数

  • mtime – znode 最后一次修改的绝对毫秒数

  • version – znode 上数据的修改数

  • cversion – 子节点修改数

  • aversion – znode 的 ACL 修改数

  • ephemeralOwner – 临时节点的所有者的 session id。如果此节点非临时节点,该值为 0

  • dataLength – znode 的数据长度

  • numChildren – znode 子节点数

ZooKeeper Sessions

客户端通过创建一个 handle 和服务端建立 session 连接。一旦创建完成,handle 就进入了 CONNECTING 状态,客户端库尝试连接一台构成 zookeeper 的 server,届时进入 CONNECTED 状态。通常情况下操作会介于这两种状态之间。一旦出现了不可恢复的错误:如 session 中止,鉴权失败或者应用直接结束 handle,则 handle 会进入到 CLOSED 状态。下图是客户端的状态转换图:

状态转换图

应用在创建客户端 session 时必须提供一串逗号分隔的主机号: 端口号,每对主机端口号对应一个 ZooKeeper 的 server(如:”127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002″),客户端库会尝试连接任意一台服务 器,如果连接失败或是客户端主动断开连接,客户端会自动继续与下一台服务器连接,直到连接成功。

3.2.0 版本新增内容:  一个新的操作“chroot”可以添加在连接字符串的尾部,用来指明客户端命令运行的根目录地址。类似 unix 的 chroot 命令,例如:“127.0.0.1:4545/app/a” or  “127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a”,说明客户端会以 ”/app/a” 为根目 录,所有路径都相对于根目录来设置,如 ”/foo/bar” 的操作会运行在 ”/app/a/foo/bar”。这一特性在多用户环境下非常好用,每个使用 zookeeper 服务的用户可以设置不同的根目录。

当客户端获得和 zookeeper 服务连接的 handle 时,zookeeper 会创建一个 Zookeeper  session 分配给客户端,用一个 64-bit 数字表示。一旦客户端连接了其他服务器,客户端必须把这个 session  id 也作为连接握手的一部分发送。出于安全目的,zookeeper 给 session id 创建一个密码,任何 zookeeper 服务器都可以验证密码。当客户端创建 session 时密码和 session id 一起发送到客户端来,当客户端重新连接其他服务器时,同时要发送密码和 session id。

zookeeper 客户端库里有一个创建 zookeeper session 的参数,叫做 session  timeout(超时),用毫秒表示。客户端发送请求超时,服务端在超时范围内响应客户端。session 超时最小为 2 个 ticktime,最大为 20 个 ticktime。zookeeper 客户端 API 可以协调超时时间。当客户端和 zookeeper 服务器集群断开时,它会搜索 session 创建时的服务器列表。最后,当至少一个服务器和客户端重新建立连 接,session 或被重新置为 ”connected” 状态(超时时间内重新连接),或被置为 ”expired(过期)” 状态(超出超时时间)。不建议在 断开连接后重新创建 session。ZK 客户端库会帮你重新连接。特别地,我们将启发式学习模式植入客户的库中来处理类似“羊群效应”等问题。只有当你的 session 过期时才重新创建(托管的)。session 过期的状态转换图示例同过期 session 的 watcher:

  1. ‘connected’ : session 正确创建,客户端和服务集群正常连接

  2. …. 客户端从服务器集群断开

  3. ‘disconnected’ : 客户端失去和服务器集群的连接

  4. …. 过了一段时间, 超过了集群判定 session 过期的超时时间, 客户端并没有发觉自己和服务集群断开了连接

  5. …. 又过一段时间, 客户端恢复了同集群的网络连接

  6. ‘expired’ : 最终客户端重新连上集群,然后被通知已经到期

另一个 session 建立时 zookeeper 需要的参数是默认 watcher(监视者)。在客户端发生任何变化时,watcher 都会发出通知。例如客户端失去和服务器的连接、客户端 session 到期等。watcher 默认的初始状态是 disconnected。(也就是说任何状态改变事件都由 客户端库发送到 watcher)。当新建一个连接时,第一个发送给 watcher 的事件通常就是 session 连接事件。

客户端发送请求会使 session 保持活动状态。客户端会发送 ping 包(译者注:心跳包)以保持 session 不会超时。Ping 包不仅让服务端 知道客户端仍然活动,而且让客户端也知道和服务端的连接没有中断。Ping 包发送时间正好可以判断是否连接中断或是重新启动一个新的服务器连接。

和服务器的连接建立成功,当一个同步或异步操作执行后,有两种情况会让客户端库判断失去连接:

  1. 应用在 session 上请求了一个已经失效的操作

  2. zookeeper 服务器有一个等待中的操作时,客户端会从那台服务器断开连接。即服务器有等待的异步调用。

3.2.0 版本新增内容 —— SessionMovedException  一个客户端无法查看的内部异常 SessionMovedException。这个异常发生在服务端收到一个请求,这个请求的 session 已经在另一个服 务器上重新连接。发生这种情况的原因通常是客户端发送完请求后,由于网络延时,客户端超时重新和其他服务器建立连接,当请求包到达第一台服务器时,服务器 发现 session 已经移除并关闭了和客户端的连接。客户端一般不用理会这个问题,但是有一种情况值得注意,当两台客户端使用事先存储的 session  id 和密码试图创建同一个连接时,第一台客户端重建连接,第二台则会被中断。

更多详情见请继续阅读下一页的精彩内容:http://www.linuxidc.com/Linux/2014-11/109661p2.htm

ZooKeeper Watches

所有 zookeeper 的读操作——getData(), getChildren(),  exists()——都可以设置一个 watch。Zookeeper 的 watch 的定义是:watch 事件是一次性触发的,发送到客户端的。在监视的数据 发生变化时产生 watch 事件。以下三点是 watch(事件)定义的关键点:

  • 一次性触发: 当数据发生变化时,一个 watch 事件被发送给客户端。例如,如果一个客户端做了一次 getData("/znode1", true) 然后节点 /znode1 发生数据变化或删除,这个客户端将收到 /znode1 的 watch 事件。如果 /znode1 继续发生改变,不会再有 watch 发送,除非客户端又做了其他读操作产生了新的 watch。

  • 发送给客户端: 这就意味着,事件在发往客户端的过程中,可能无法在修改操作成功的返回值到达客户端之前到达客户端。watch 是异步发送给 watchers 的。zookeeper 提供一种保证顺序的方法:客户端在第一次看到某个 watch 事件之前不可能看到产生 watch 的修改的返回值。网络延时或其他因素可能 导致不同客户端看到 watch 并返回不同时间更新的返回值。关键的一点是,不同的客户端看到发生的一切都必须是按照相同顺序的。

  • watch 依附的数据: 这是说改变一个节点有不通方式。用好理解的话说,zookeeper 维护两组 watch:data watch 和 child  watch。getData()和 exists()产生 data watch。getChildren()引起 child  watch。watch 根据数据返回的种类不同而不同。getData()和 exists()返回关于节点的数据信息,而 getChildren()返回 子节点列表。因此 setData()触发某个 znode 的 data  watch(假设事件成功)。create()成功会触发被创建的 znode 上的 data watch 和在它父节点上的 child  watch。delete()成功会触发 data watch 和 child watch(因为没有了子节点)。

watch 在客户端已连接上的服务器里维护,这样可以保证 watch 轻量便于设置,维护和分发。当客户端连接了一台新的服务器,watch 会在任何 session 事件时触发。当断开和服务器的连接时,watch 不会触发。当客户端重新连接上时,任何之前注册过的 watch 都会重新注册并在需要的时候 被触发。一般来说这一切都是透明的。只有一种可能会丢失 watch:当一个 znode 在断开和服务器连接时被创建或删除,那么判断这个 znode 存在的 watch 因未创建而找不到。

ZooKeeper 如何保证 watch 可靠性

zookeeper 有如下方式:

  • watch 与其他事件、watch、异步回复保持有序,Zookeeper 客户端库确保任何分发都是有序的。

  • 客户端会在某个监视的 znode 数据更新之前看到这个 znode 的 watch 事件。

  • watch 事件的顺序由 Zookeeper 服务端观察到的更新顺序决定。

watch 注意事项

  • watch 是一次性触发的;如果你收到 watch 事件后还想继续得到后续更改的通知,你需要再生成(设置)一个 watch。

  • 由于 watch 是一次性触发,你在获取某事件和发送新的请求来得到 watch 这个操作之间,无法确保观察到 Zookeeper 中那个节点在这期间 的所有修改。你要准备好应付这种情况出现:znode 会在收到事件和再次设置新事件(译者注:对节点的操作)之间发生了多次修改。(你可能并不关心,但是 必须了解这可能发生)

  • watch 对象,或是 function/context 对,只会在得到通知时触发一次。例如,如果一个 watch 对象同时用来监控某个目标文件是否存在和监听 getData(),之后那个文件被删除了。那么这个 watch 对象只会触发一次文件删除事件通知。

  • 如果你断开了同服务器的连接(例如服务器挂了),你在重新连上之前得不到任何 watch。出于这种原因,session  event 会被发送给所有重要的 watch  handler。可以使用 session 事件进入安全模式:当断开连接时你收不到任何事件,这样你的进程可以在那种模式下稳健地执行。(译者注:可以通过 发送 session event 使客户端进入安全模式(伪断开连接状态),在安全模式你可以修改代码而不用担心程序收到事件通知)

 

使用 ACL 控制 ZooKeeper 访问权限

zookeeper 使用 ACL 来控制对 znode(zookeeper 的数据节点)的访问权限。ACL 的实现方式和 unix 的文件权限类似:用不同 位来代表不同的操作限制和组限制。与标准 unix 权限不同的是,zookeeper 的节点没有三种域——用户,组,其他。zookeeper 里没有节点的 所有者的概念。取而代之的是,一个由 ACL 指定的 id 集合和其相关联的权限。注意,一个 ACL 只从属于一个特定的 znode。对这个 znode 子节点也是无效的。例如,如果 /app 只有被 ip172.16.16.1 的读权限,/app/status有被所有人读的权限,那么 /app/status 可以被所有人读,ACL 权限不具有递归性。zookeeper 支持插件式认证方式,id 使用 scheme:id 的形式。scheme是 id 对应的类型方式,例如 ip:172.16.16.1 就是一个地址为 172.16.16.1 的主机 id。当客户端连接 zookeeper 并且认证自己,zookeeper 就在这个与客户端的连接中关联所有与客户端一致的 id。当客户端访问某个 znode 时,znode 的 ACL 会重新检查这些 id。ACL 的表达式为 (scheme:expression,perms)expression 就是特殊的 scheme,例如,(ip:19.22.0.0/16, READ)就是把任何以 19.22 开头的 ip 地址的客户端赋予读权限。

 

ACL 权限

ZooKeeper 支持下列权限:

  • CREATE:允许创建子节点

  • READ:允许获得节点数据并列出所有子节点

  • WRITE:允许设置节点上的数据

  • DELETE:允许删除子节点

  • ADMIN:允许设置权限

CREATE 和 DELETE 操作是更细的粒度上的 WRITE 操作。有一种特殊的情况:

  • 你想要 A 获得操作 zookeeper 上某个 znode 的权限,但是不可以对其子节点进行 CREATE 和 DELETE。

  • 只 CREATE 不 DELETE:某个客户端在上一级目录上通过发送创建请求创建了一个 zookeeper 节点。你希望所有客户端都可以在这个节点上添加,但是只有创建者可以删除。(这就类似于文件的 APPEND 权限)

zookeeper 没有文件所有者的概念,但有 ADMIN 权限。在某种意义上说,ADMIN 权限指定了所谓的所有者。zookeeper 虽然不支持 查找权限(在目录上的执行权限虽然不能列出目录内容,却可以查找),但每个客户端都隐含着拥有查找权限。这样你可以查看节点状态,但仅此而已。(这有个问 题,如果你在不存在的节点上调用了 zoo_exists(),你将无权查看)

 

内建 ACL 模式

ZooKeeper 有下列内建模式:

  • world  有独立 id,anyone,代表任何用户。

  • auth 不使用任何 id,代表任何已经认证过的用户

  • digest 之前使用了格式为 username:pathasowrd 的字符串来生成一个 MD5 哈希表作为 ACL ID 标识。在空文档中发送 username:password 来完成认证。现在的 ACL 表达式格式为username:base64, 用 SHA1 编码密码。

  • ip 用客户端的 ip 作为 ACL ID 标识。ACL 表达式的格式为addr/bits,addr 中最有效的位匹配上主机 ip 最有效的位。

 

ZooKeeper C client API

 

插件式 ZooKeeper 认证

zookeeper 运行于复杂的环境下,有各种不同的认证方式。因此 zookeeper 拥有一套插件式的认证框架。内建认证 scheme 也是使用这 套框架。为了便于理解认证框架的工作方式,你首先要了解两种主要的认证操作。框架首先必须认证客户端。这步操作通常在客户端连接服务器的同时完成并且将从客户端发 过来的(或从客户端收集来的)认证信息关联此次连接。认证框架的第二步操作是在 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 服务器使用认证插件返回的 scheme 判断哪个 id 适用于该 scheme。当客户端发送与连接关联的认证信息时,handleAuthentication 被调用。客户端指定和认证信息相应的模式。zookeeper 把信息传给认证插件,认证插件的 getScheme 匹配 scheme。实现 handleAuthentication 的方法通常在判断信息错误后返回一个 error,或者在确认连接后使用cnxn.getAuthInfo().add(new Id(getScheme(), data))

认证插件在设置和 ACL 中都有涉及。当对某个节点设置 ACL 时,zookeeper 服务器会传那个条目的 id 给 isValid(String id) 方法。插件需要判断 id 的连接来源。例如,ip:172.16.0.0/16是有效 id,ip:host.com 是无效 id。如果新的 ACL 包括一个 ”auth” 条目,就用 isAuthenticated 判断该 scheme 的认证信息是否关联了连接,是否可以被添加到 ACL 中。一些 scheme 不会被包含到 auth 中。例如,如果 auth 已经指定,客户端的 ip 地址就不作为 id 添加到 ACL 中。在检查 ACL 时 zookeeper 有一个 matches(String id, String aclExpr) 方法。ACL 的条目需要和认证信息相匹配。为了找到和客户端对应的条目,zookeeper 服务器寻找每个条目的 scheme,如果对某个 scheme 有那个客户端的认证信息,matches(String id, String aclExpr)会被调用并传入两个值,分别是事先由handleAuthentication 加入连接信息中认证信息的 id,和设置到 ACL 条目 id 的aclExpr。认证插件用自己的逻辑匹配 scheme 来判断 id 是否在 aclExpr 中。

有两个内置认证插件: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,只有一个会被使用。同样,所有服务器都必须统一插件定义,否则客户端用插件提供的认证 schemes 连接服务器时会出错。

 

一致性保证

ZooKeeper 是一个高性能,可扩展的服务。读和写操作都非常快速。之所以如此,全因为 zookeeper 有数据一致性的保证:

顺序一致性 客户端的更新会按照它们发送的次序排序。

原子性 更新的失败或成功,都不会出现半个结果。

单独系统镜像 不管客户端连哪个服务器,它看来都是同一个。

可靠性 一旦更新生效,它就会一直保存到下一次客户端更新。这就有两个推论:

  1. 如果客户端得到成功的返回值,说明更新生效了。在一些错误情况下(连接错误,超时等)客户端不会知道更新是否生效。虽然我们使失败的几率最小化,但是也只能保证成功的返回值情况。(这就叫 Paxos 算法的单调性条件)

  2. 客户端能看到的更新,即使是渡请求或成功更新,在服务器失败时也不会回滚。

时效性 客户端看到的系统状态在某个时间范围内是最新的(几十秒内),任何系统更改都会在该时间范围内被客户端发现。否则客户端会检测到断开服务。

用这些一致性保证可以在客户端中构造出更高级的程序如 leader election, barriers, queues, read/write revocable locks(无须在 zookeeper 中附加任何东西)。更多信息 Recipes and Solutions

zookeeper 不存在的一致性保证:多客户端同一时刻看到的内容相同 zookeeper 不可能保证两台客户端在同一时间看到的内容总是一样,由于网络延迟等原因。假设这样一个场景,A 和 B 是两个客户端,A 设置节点 / a 下的 值从 0 变为 1,然后让 B 读 /a,B 可能读到旧的数据 0。如果想让 A 和 B 读到同样的内容,B 必须在读之前调用 zookeeper 接口中的 sync()方法。

 

编程接口

 

常见问题和故障

下面是一些常见的陷阱:

  1. 如果你使用 watch,你必须监控好已经连接的 watch 事件。当 ZooKeeper 客户端断开和服务器的连接,直到重新连接上这段时间你都收不到任何通知。如果你正在监视 znode 是否存在,那么你在断开连接期间收不到它创建和销毁的通知。

  2. 你必须测试 ZooKeeper 故障的情况。在大多数服务器都可用的情况下,ZooKeeper 是可以维持工作的。关键问题是你的客户端程序是否能 察觉到。在实际情况下,客户端与 ZooKeeper 的连接有可能中断(多数时候是因为 Zookeeper 故障或网络中断)。Zookeeper 的客户端库 关注于如何让你重新连接并且知道发生了什么。但是同时你也必须确保能够恢复你的状态和发送失败的请求。努力在测试库里测出这些问题,而不是在产品里——用 几台服务器组成的 zookeeper 集群测试这个问题,尝试让它们重启。

  3. 客户端维护的服务器列表必须和现有的服务器列表一致。如果客户端的列表是现有服务器列表的子集,还可以在非最佳状态工作,但是如果客户端列表里的服务器不在现有集群里你就悲剧了。

  4. 注意存放事务日志的位置。性能评测最重要的部分就是日志,ZooKeeper 会在回复响应之前先把日志同步到磁盘上。为了达到最佳性能,首选专用 的磁盘来存日志。把日志放在繁忙的磁盘上会降低效率。如果你只有一个磁盘,就把记录文件放在 NFS 上然后增加 SnapshotCount。这样虽然无法完 全解决问题,但能缓解一些。

  5. 正确地设置你 Java 的堆空间大小。这是避免频繁交换的有效措施。无用的访问磁盘会让你的效率大打折扣。记住,在 ZooKeeper 中,一切都是有序的,如果一个服务器访问了磁盘,所有的服务器都会同步这个操作。

其他资料链接请自行官网查看。

摘要 自己翻译的 ZooKeeper 官方文档,需要的人可以参考一下,有翻译错误还请指正。

目录

ZooKeeper 的数据模型
ZNodes
ZooKeeper 中的时间
ZooKeeper 状态结构(Stat Structure)
ZooKeeper Sessions
ZooKeeper Watches
使用 ACL 控制 ZooKeeper 访问权限
ACL 权限
内建 ACL 模式
ZooKeeper C client API
插件式 ZooKeeper 认证
一致性保证
编程接口
常见问题和故障

翻译:steve sun 博客地址:http://sundiontheway.github.io/blog

————————————– 分割线 ————————————–

ZooKeeper 集群配置 http://www.linuxidc.com/Linux/2013-06/86348.htm

使用 ZooKeeper 实现分布式共享锁 http://www.linuxidc.com/Linux/2013-06/85550.htm

分布式服务框架 ZooKeeper — 管理分布式环境中的数据 http://www.linuxidc.com/Linux/2013-06/85549.htm

ZooKeeper 集群环境搭建实践 http://www.linuxidc.com/Linux/2013-04/83562.htm

ZooKeeper 服务器集群环境配置实测 http://www.linuxidc.com/Linux/2013-04/83559.htm

ZooKeeper 集群安装 http://www.linuxidc.com/Linux/2012-10/72906.htm

————————————– 分割线 ————————————–

本文假设你已经具有一定分布式计算的基础知识。你将在第一部分看到以下内容:

  • ZooKeeper 数据模型

  • ZooKeeper Sessions

  • ZooKeeper Watches

  • 一致性保证(Consistency Guarantees)

接下来的 4 小节讲述了程序开发的实际应用:

  • 创建模块——ZooKeeper 操作指引

  • 编程语言接口

  • 简单示例演示程序的结构

  • 常见问题和故障

本文的附录中包含和 ZooKeeper 相关的有用信息。

ZooKeeper 的数据模型

ZooKeeper 有一个类似分布式文件系统的命名体系。区别在于 Zookeeper 每个一个节点或子节点都可以拥有数据。节点路径是一个由斜线分开的绝对路径,注意没有相对路径。只要满足下面要求的 unicode 字符都可以作为节点路径:

  • 空字符不能出现在路径名

  • 不能出现以下字符: \u0001 – \u0019 and \u007F – \u009F

  • 以下字符不允许使用: \ud800 -uF8FFF, \uFFF0-uFFFF, \uXFFFE – \uXFFFF (where X is a digit 1 – E), \uF0000 – \uFFFFF

  • 字符 ”.” 可以作为一个名字的一部分, 但是 ”.” 和 ”..” 不能单独作为相对路径使用, 以下用法都是无效的: “/a/b/./c” 或者 ”/a/b/../c”

  • “zookeeper” 为保留字符

ZNodes

ZooKeeper 树结构中的节点被称为 znode。各个 znode 维护着一组用来标记数据和访问权限发生变化的版本号。这些版本号组成的状态结构 具有时间戳。Zookeeper 使用版本号和时间戳来验证缓存状态,调整更新。每次 znode 中的数据发生变化,znode 的版本号增加。例如,每当一个客户端恢复数据时,它就接收这个版本的数据,而当一个客户端提交了更新或删除记 录,它必须同时提供这个 znode 当前正在发生变化的数据的版本。如果这个版本和目前真实的版本不匹配,则提交无效。__提示,在分布式程序中,一个字节点可以代表一个通用的主机,服务器,集群中的一员,客户端程序等。但是在 Zookeeper 中,znode 代表数据节 点,Servers 代表组成了 Zookeeper 服务的机器; quorum peers refer to the servers that  make up an ensemble; 客户端代表任何使用 ZooKeeper 服务的主机或程序。

znode 作为对程序开发来说最重要的信息,有几个特性需要特别关注下:

Watches 客户端可以在 znode 上设置 Watch。znode 发生的变化会触发 watch 然后清除 watch。当一个 watch 被触发,Zookeeper 给客户端发送一个通知。更多关于 watch 的内容请查看 ZooKeeper Watches 一节。

数据存取 命名空间中每个 znode 中的数据读写是原子操作。读操作读取 znode 中的所有数据位,写操作则替换所有数据。每个节点都有一个访问权限控制表(ACL)来标记谁可以做什么。zookeeper 不是设计成普通的数据库或大型对象存储的。它是用来管理 coordination data。coordination  data 包括配置文件、状态信息、rendezvous 等。这些数据结构的一个共同特点就是相对较小——以千字节为准。Zookeeper 的客户端和服务 会检查确保每个 znode 上的数据小于 1M,实际平均数据要远远小于 1M。大规模数据的操作会引发一些潜在的问题并且延长在网络和介质之间传输的时间。如果确实需要大型数据的存储,那么可以采用如 NFS 或 HDFS 之类的大型数据 存储系统,亦或是在 zookeeper 中存储指向存储位置的指针。

临时节点(Ephemeral Nodes) zookeeper 还有临时节点的概念,这些节点的生命周期依赖于创建它们的 session 是否活跃。session 结束时节点即被销毁。也由于这种特性,临时节点不允许有子节点。

序列节点——命名不唯一 当你创建节点的时候,你会需要 zookeeper 提供一组单调递增的计数来作为路径结尾。这个计数对父 znode 是唯一的。用%010d 的格式——用 0 来填充的 10 位数(计数如此命名是为了简单排序)。例如 ”0000000001″,注意计数器是有符号整型,超过表示范围会溢出。

ZooKeeper 中的时间

zookeeper 有很多记录时间的方式:

  • Zxid(ZooKeeper Transaction Id):zookeeper 每次发生改动都会增加 zxid,zxid 越大,发生的时间越靠后。

  • Version numbers:对 znode 的改动会增加版本号。版本号包括 version (znode 上数据的修改数), cversion (znode 的子节点的修改数), aversion (znode 上 ACL(权限)的修改数)。

  • Ticks : 多个 server 构成 zookeeper 服务时,各个 server 用 ticks 来标记如状态上报、连接超时等事件。ticks  time 还间接反映了 session 超时的最小值(两次 tick time);如果客户端请求的最小 session  timeout 低于这个最小值,服务端会通知客户端最小超时置为这个最小值。

  • Real time : 除了每次 znode 创建或改动时候将时间戳记录到状态结构中外,zookeeper 不使用时钟时间。

ZooKeeper 状态结构(Stat Structure)

存在于 znode 中的状态结构,由以下各个部分组成:

  • czxid – znode 创建产生的 zxid

  • mzxid – znode 最后一次修改的 zxid

  • ctime – znode 创建的时间的绝对毫秒数

  • mtime – znode 最后一次修改的绝对毫秒数

  • version – znode 上数据的修改数

  • cversion – 子节点修改数

  • aversion – znode 的 ACL 修改数

  • ephemeralOwner – 临时节点的所有者的 session id。如果此节点非临时节点,该值为 0

  • dataLength – znode 的数据长度

  • numChildren – znode 子节点数

ZooKeeper Sessions

客户端通过创建一个 handle 和服务端建立 session 连接。一旦创建完成,handle 就进入了 CONNECTING 状态,客户端库尝试连接一台构成 zookeeper 的 server,届时进入 CONNECTED 状态。通常情况下操作会介于这两种状态之间。一旦出现了不可恢复的错误:如 session 中止,鉴权失败或者应用直接结束 handle,则 handle 会进入到 CLOSED 状态。下图是客户端的状态转换图:

状态转换图

应用在创建客户端 session 时必须提供一串逗号分隔的主机号: 端口号,每对主机端口号对应一个 ZooKeeper 的 server(如:”127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002″),客户端库会尝试连接任意一台服务 器,如果连接失败或是客户端主动断开连接,客户端会自动继续与下一台服务器连接,直到连接成功。

3.2.0 版本新增内容:  一个新的操作“chroot”可以添加在连接字符串的尾部,用来指明客户端命令运行的根目录地址。类似 unix 的 chroot 命令,例如:“127.0.0.1:4545/app/a” or  “127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a”,说明客户端会以 ”/app/a” 为根目 录,所有路径都相对于根目录来设置,如 ”/foo/bar” 的操作会运行在 ”/app/a/foo/bar”。这一特性在多用户环境下非常好用,每个使用 zookeeper 服务的用户可以设置不同的根目录。

当客户端获得和 zookeeper 服务连接的 handle 时,zookeeper 会创建一个 Zookeeper  session 分配给客户端,用一个 64-bit 数字表示。一旦客户端连接了其他服务器,客户端必须把这个 session  id 也作为连接握手的一部分发送。出于安全目的,zookeeper 给 session id 创建一个密码,任何 zookeeper 服务器都可以验证密码。当客户端创建 session 时密码和 session id 一起发送到客户端来,当客户端重新连接其他服务器时,同时要发送密码和 session id。

zookeeper 客户端库里有一个创建 zookeeper session 的参数,叫做 session  timeout(超时),用毫秒表示。客户端发送请求超时,服务端在超时范围内响应客户端。session 超时最小为 2 个 ticktime,最大为 20 个 ticktime。zookeeper 客户端 API 可以协调超时时间。当客户端和 zookeeper 服务器集群断开时,它会搜索 session 创建时的服务器列表。最后,当至少一个服务器和客户端重新建立连 接,session 或被重新置为 ”connected” 状态(超时时间内重新连接),或被置为 ”expired(过期)” 状态(超出超时时间)。不建议在 断开连接后重新创建 session。ZK 客户端库会帮你重新连接。特别地,我们将启发式学习模式植入客户的库中来处理类似“羊群效应”等问题。只有当你的 session 过期时才重新创建(托管的)。session 过期的状态转换图示例同过期 session 的 watcher:

  1. ‘connected’ : session 正确创建,客户端和服务集群正常连接

  2. …. 客户端从服务器集群断开

  3. ‘disconnected’ : 客户端失去和服务器集群的连接

  4. …. 过了一段时间, 超过了集群判定 session 过期的超时时间, 客户端并没有发觉自己和服务集群断开了连接

  5. …. 又过一段时间, 客户端恢复了同集群的网络连接

  6. ‘expired’ : 最终客户端重新连上集群,然后被通知已经到期

另一个 session 建立时 zookeeper 需要的参数是默认 watcher(监视者)。在客户端发生任何变化时,watcher 都会发出通知。例如客户端失去和服务器的连接、客户端 session 到期等。watcher 默认的初始状态是 disconnected。(也就是说任何状态改变事件都由 客户端库发送到 watcher)。当新建一个连接时,第一个发送给 watcher 的事件通常就是 session 连接事件。

客户端发送请求会使 session 保持活动状态。客户端会发送 ping 包(译者注:心跳包)以保持 session 不会超时。Ping 包不仅让服务端 知道客户端仍然活动,而且让客户端也知道和服务端的连接没有中断。Ping 包发送时间正好可以判断是否连接中断或是重新启动一个新的服务器连接。

和服务器的连接建立成功,当一个同步或异步操作执行后,有两种情况会让客户端库判断失去连接:

  1. 应用在 session 上请求了一个已经失效的操作

  2. zookeeper 服务器有一个等待中的操作时,客户端会从那台服务器断开连接。即服务器有等待的异步调用。

3.2.0 版本新增内容 —— SessionMovedException  一个客户端无法查看的内部异常 SessionMovedException。这个异常发生在服务端收到一个请求,这个请求的 session 已经在另一个服 务器上重新连接。发生这种情况的原因通常是客户端发送完请求后,由于网络延时,客户端超时重新和其他服务器建立连接,当请求包到达第一台服务器时,服务器 发现 session 已经移除并关闭了和客户端的连接。客户端一般不用理会这个问题,但是有一种情况值得注意,当两台客户端使用事先存储的 session  id 和密码试图创建同一个连接时,第一台客户端重建连接,第二台则会被中断。

更多详情见请继续阅读下一页的精彩内容:http://www.linuxidc.com/Linux/2014-11/109661p2.htm

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-20发表,共计17609字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中