原文地址: http://www.importnew.com/20457.html
简介
对于想要利用ZooKeeper的协调服务来创建一个分布式应用的开发人员来说,这篇文章提供了指导。包含了一些概念和实际性操作的信息。
这篇文章的前四个章节介绍了各种ZooKeeper的概念,这对理解ZooKeeper是怎么工作的是必须的。没有包含源代码,但是它假设你对分布式处理有关的问题比较熟悉。这四个章节是:
ZooKeeper数据模型
ZooKeeper 会话
ZooKeeper Watches
一致性保证
随后的四个章节提供了实际的编程信息,他们是:
构建块:ZooKeeper操作指南
绑定
程序结构,简单的例子
陷阱:常见问题和故障排查
ZooKeeper数据模型
ZooKeeper有一个层级命名空间,和一个分布式文件系统非常相似。唯一的不同是每个节点可以有关联的数据,子节点也是。就像有一个文件系统并且允许文件是一个目录。一个规范的、绝对的、斜杠分隔的路径来表示一个节点路径,没有相对路径。任何符合下列约束的的unicode字符可以被使用:
null字符串(\u0000)不能是一个路径名称。
下列字符不能被使用,因为不能很好的被展示:\u0001 – \u001F 和 \u007F – \u009F。
下列字符是不允许的:\ud800 – uF8FF, \uFFF0 – uFFFF。
“.”字符可以作为另一个名字被使用,但是“.”和“..”不能单独使用来表示一个节点路径,因为ZooKeeper不使用相对路径,下列是无效的:”/a/b/./c”或者 “/a/b/../c”。
“zookeeper”标记被保留。
ZNodes
在ZooKeeper树中的每个节点被称为一个znode。Znodes包含了一个stat数据结构,这个数据结构包括了数据变更的版本号、acl变更。stat数据结构也有时间戳,版本号和时间戳一起来允许ZooKeeper校验缓存和协调更新。每当一个znode的数据改变,版本号就会增加。例如:当一个客户端取得数据,它同样也接受数据的版本。并且,当一个客户端执行一个更新或删除操作,它必须提供数据的版本号。如果客户端提供的的版本号和实际的版本号不匹配,更新操作将会失败。
注意 在分布式应用应用中,<em>node</em>一词可以用来表示一台主机、一个服务器、集中中的一个、一个客户端进程等等。早ZooKeeper这边文档中,<em>znodes </em>表示一个数据节点,<em>Servers</em>表示组成ZooKeeper服务中的机器,<em>quorum peers</em> 表示组成集合的机器,客户端表示使用一个ZooKeeper服务的主机或进程。
Znodes是一个程序员访问的主要实体,有许多在这里值得提到特性:
Watches
客户端可以在znodes上设置监听器,znode的改变触发这个监听器然后清空这个监听器。当一个监听器被触发,ZooKeeper发送给客户端一个通知。更多信息可以查看ZooKeeper Watches章节。
数据访问
每个znode的上存储的数据读写都是原子的,读操作取出所有的和这个znode有关的所有数据,写操作替换所有的数据。每个节点有一个访问权限列表(ACL)来限制谁可以做这些事情。
ZooKeeper没有被设计成一个一般的数据库或一个大型对象存储。它管理协调数据,数据可以是配置、状态信息、集合点等的形式。各种各样的数据有一个共同的属性就是他们都很小:以千字节为标准。ZooKeeper客户端和服务器有一个健康检查来确保znodes的数据少于1M,但是数据平均应该更小。操作较大的数据将导致一些操作花费更多的时间,并且会影响一些操作的延迟,因为在网络和存储媒介中移动更多的数据将需要额外的时间。如果需要存储大数据,通常的处理是把数据存储在一个大容量存储系统中,并把存储位置的指针存储到ZooKeeper上。
临时节点
ZooKeeper也临时节点的概念。这些znodes存活的时间和创建这个节点的会话有效期是一样的。当会话结束,节点被删除。因为这种临时节点的特性,临时节点不允许有子节点。
顺序节点——唯一名称
当创建一个节点的时候,也可以请求ZooKeeper在路径后面增加一个自增的计数器。对父节点来说,这个计数器是 唯一的。计数器是%010d的格式——是一个十位数,比如:<path>0000000001。
查看Queue Recipe使用这个特性的示例,注意:这个计数器用来存储下一个序列号是一个4字节的数,当增加到2147483647 之后,计数器会溢出。
ZooKeeper中的时间
ZooKeeper以多种方式跟踪时间:
Zxid:ZooKeeper状态的每次变化都接收一个zxid(ZooKeeper事务id)形式的标记。这个展示了所有的ZooKeeper的变更顺序。每次变更会有一个唯一的zxid,如果zxid1小于zxid2说明zxid1在zxid2之前发生。
Version numbers:节点的每次变化都会引起这个节点版本号之一的一次增加。这三个版本号是:version(一个节点的数据变化次数),cversion(一个节点的子节点变化次数),aversion(一个节点的ACL 变化次数)。
Tricks:当使用多个ZooKeeper服务,服务器使用ticks来确定事件的时间,比如说状态上传、会话超时、连接超时等。这个tick时间仅仅通过最小会话超时时间间接的暴露出来;如果一个客户端请求会话的超时时间小于最小超时时间,服务器将会告诉客户端实际的会话超时时间是最小超时时间。
Real Time:ZooKeeper不使用实时、时钟时间。除了把时间戳放在stat结构中。
ZooKeeper Stat 结构
每个节点的Stat结构由下列字段组成:
czxid:该数据节点被创建时的事务id。
mzxid:该节点最后一次被更新时的事务id。
ctime:节点被创建时的时间。
mtime:节点最后一次被更新时的时间。
version:这个节点的数据变化的次数。
cversion:这个节点的子节点 变化次数。
aversion:这个节点的ACL变化次数。
ephemeralOwner:如果这个节点是临时节点,表示创建者的会话id。如果不是临时节点,这个值是0。
dataLength:这个节点的数据长度。
numChildren:这个节点的子节点个数。
ZooKeeper会话
通过使用一种语言绑定来创建服务端的句柄,一个ZooKeeper客户端可以和ZooKeeper服务创建会话。一旦创建,句柄开始在CONNECTING 状态,客户端库尝试连接组成ZooKeeper服务中的其中一个服务器并且切换到CONNECTED状态。在正常的操作期间将会是这两种状态之一。如果一个不可恢复的错误发生了,比如说会话过期或授权失败,或者如果应用显示地关闭了句柄,句柄将会到CLOSED状态。下面的图展示了一个ZooKeeper客户端可能的状态转变。
为了创建一个客户端会话,应用程序代码必须提供一个连接字符串列表以逗号分隔开,主机:端口号成对出现,每个都相当于一个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”后缀可以被加在连接字符串后面,这会运行客户端命令导致所有的路径都和这个跟路径相关。如果使用像下面的示例:”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”作为跟路径,并且所有的路径都与这个根路径相关,比如getting、setting等。”/foo/bar” 将导致操作在”/app/a/foo/bar”(从服务端的观点来看)。这个特性在多租户下面是非常也有用的,ZooKeeper服务的每个用户可以有不同的根路径。这让再使用变得非常简单,因为每个用户都可以编写代码让他的应用好像在”/”根路径下,但实际的位置能在部署时决定。
当一个客户端从ZooKeeper服务得到一个句柄,ZooKeeper创建了一个会话,表现为一个64位的数字,并把 它分配给客户端。如果客户端连接到一个不同的服务端,在连接握手的时候它将发送这个会话id。作为一个安全措施,服务端给会话id创建了一个密码,让服务端能够校验。当客户端建立会话的时候,这个密码随着会话id一起发送给客户端。每当客户端与一个新的服务端恢复会话的时候,密码会随着会话id一起发送过去。
客户端调用创建会话的时候有一个参数是会话超时时间(毫秒),客户端发送一个要求的超时时间,服务端回复一个他能给客户端的超时时间。当前实现要求超时时间至少是2倍的tickTime,最大是20倍的tickTime。ZooKeeper客户端API允许使用一个协商的超时时间。
当一个客户端从ZK服务集群成为分区,它将开始寻找在会话创建时期指定的服务端列表。最终,当客户端和至少一个服务端联通重新建立的时候,会话要么转变成“connected”状态(如果在会话超时时间内恢复连接),要么转变成“expired”状态(如果在超时时间之外恢复连接)。在断开时创建一个新的会话是不可取的。ZK客户端库将处理连接。尤其是客户端内部有方法来处理像“羊群效应”之类的事情。仅仅在你被通知会话过期的时候去创建一个新的会话。
ZooKeeper集群自己管理会话过期,而不是由客户端管理。当ZK客户端和一个集群建立会话,它提供一个“超时时间”。这个值被集群使用来决定客户端的会话是否过期。当集群不能在指定的会话超时时间内从客户端收到信息,过期发生。在会话过期期间,集群将删除由这个会话创建的所有的临时节点,并且立即通知连接的客户端这个改变。此时,会话过期的客户端依然和集群式断开的,它不会收到通知直到它能和集群重新建立连接。这个客户端将保持断开状态直到和集群的TCP连接重新建立,并且在这个时候,过期会话的监听将会收到“会话过期”通知。
对于一个过期的会话,监听器所看到的状态转变:
“connected”:会话被建立,并且客户端能和集群交流
……客户端从集群被分割
“disconnected”:客户端与集群丢失了联系
……时间流逝,在超时时间之后,集群已经让这个会话过期,而客户端没看到什么,因为它已经从集群断开连接了
……时间流逝,客户端恢复网络和集群联通
“expired”:最后客户端与集群重新连接,然后收到过期的通知
ZooKeeper会话建立的另一个参数是默认监听器。当客户端的一些状态改变发生,监听器会收到通知。比如如果客户端丢失与服务端的连接,客户端将会收到通知,或客户端的会话到期等。这个监听器应该考虑初始状态到断开连接。对于一个新的连接,第一给发给监听器的事件就是会话连接事件。
客户端通过发送请求保持会话存活。如果会话在一段时间内空闲将会导致会话超时,客户端将会发送PING请求保持会话存活。这个PING请求不仅仅让ZooKeeper服务端知道客户端是存活的,而且让客户端检查它的和ZooKeeper 服务端的连接也是存活的。PING的时间是足够保守的合理时间,来发现死掉的连接和一个新的服务端重新连接。
一旦成功建立一个到服务端的连接,当客户端发生connectionloss异常 时有两种基本的情况,在执行一个同步或者非同步的操作时:
应用调用一个操作,但是会话不再存活。
当等待一个操作的时候ZooKeeper客户端从服务端断开连接,比如说:等待一个异步调用。
3.2.0新增——SessionMovedException。有一个内部的异常,通常不会被客户端发现,被称为SessionMovedException。一个已经连接的会话但是重新连接到了一个不同的服务器上接收了一个请求,这个异常就会发生。这个错误的正常原因是一个客户端发送了一个请求到一个服务端,但是网络数据包延迟了,所以客户端超时并连接到了一个新的服务器。当延迟的数据包到达了第一个服务器,这个服务端发现这个会话已经被移除了并且关闭了这个客户端连接。客户端一般不会发现这个错误因为它们不在从老的连接读取数据(老的连接一般被关闭了)。这种事情发生的另一种情况是当两个客户端使用一个保存的会话id和密码来尝试恢复相同的连接时,只有一个客户端能够恢复连接,另一个客户端将会断开。
更新服务器列表。我们允许一个客户端更新连接字符串通过提供一个新的逗号分隔的主机:端口号列表,每个都是一个服务器。函数调用一个概率负载均衡算法会引起客户端断开与当前主机的连接,来使在新列表中的每个服务器达到与预期一致的数量。万一客户端连接的当前主机不在新的列表中,这个调用会引起连接被删除。另外,这个决定基于是否服务器的数量增加或减少了多少。
比如说,如果之前的连接包含三个主机,现在的连接多了两个主机,连接到每个主机的客户端的40%为了负载均衡将会移动到新的主机上去。这个算法会引起客户端断掉它当前与服务器的连接,这个概览是0.4,并且客户端随机选择两个新主机中的一个连接。
另一个例子,假设我们有5个主机,然后现在更新列表移除两个主机,连接到剩余三台主机的客户端依然保持连接,然而所有连接到已被移除主机的客户端都需要移到剩下三台主机的一台上,并且这种选择是随机的。如果连接断开,客户端进入一个特殊的模式并使用概率算法选择一个新的服务器,而不仅仅只是循环。
在第一个例子中,每个客户端决定断开连接的概览为0.4,但是一旦做了决定,它将会随机的连接到一个新的服务器,仅仅当它不能连接到任何一台新的服务器上时,它将尝试连接旧的服务器。当找到一个服务器或者新列表中所有的服务器都连接失败的时候,客户端回到操作正常模式,选择一个任意的服务器并尝试连接它,如果连接失败,它会继续尝试不同的随机的服务器,并一直循环下去。
ZooKeeper Watches
ZooKeeper中所有的读操作——getData(), getChildren()和 exists()—可以选择设置 一个监听器。这是ZooKeeper’s一个监听器的定义:一个监听事件是一次性触发,当一个被设置监听的数据改变时,发送给设置这个监听器的客户端。在这个监听器的定义中,有三个要点:
一次性触发:当数据改变的时候一个监听事件会被发送给客户端。比如说,如果一个客户端做了getData(“/znode1″, true)操作,然后 /znode1下的数据被改变或者删除了,客户端将得到/znode1的一个监听事件。如果/znode1节点再次发生改变,没有监听事件会被发送除非客户端做了别的设置了一个新的监听器。
发送到客户端:这意味着事件正在发送给客户端的途中,但是在操作成功的返回码到达发起这个变更操作的客户端之前,事件可能还没到达监听的客户端。ZooKeeper提供了一个有序保证:在它第一次看到监听事件之前,它永远不会看到它设置的监听改变。网络延迟或别的因素可能会引起不同的客户端看见监听器和更新操作的返回码,在不同的时间。关键得一点是不同的客户端看见的每件事有一个一致的顺序。
被设置监听的数据:这是指一个节点能变化的不同方式。可以认为ZooKeeper有两个监听器列表:数据监听和子节点监听。getData()和exists()设置数据监听器。 getChildren()设置子节点监听器。二选一,根据返回数据的类型来设置监听器。getData()和exists()返回节点的数据信息,然而getChildren()返回一个子节点列表。因此,setData()会触发数据监听器。一个成功的 create()会触发一个数据监听器。一个delete()会触发数据监听器和子节点监听器。
在ZooKeeper服务器中,当客户端连接的时候,监听器被保存在本地。这使得监听器轻量级的被设置、保存、分发。当一个客户端连接一个新的服务器,监听器会触发一些会话事件。当从服务器断开连接的时候,不会受到监听器。当一个客户端重新连接,如果需要的话,之前注册的监听器会被注册和触发。有一个监听器可能丢失的情况:如果在断开连接期间,一个节点被创建和删除,一个已存在的节点的监听器还没有创建,将丢失。
监听器的意思
我们能在三种调用读取ZooKeeper状态的情况下设置监听器:exists,getData和getChildren,下面的列表是一个监听器触发的事件的详细情况:
创建事件:exists的调用
删除事件:exists,getData和getChildren的调用
改变事件:exists,getData的调用
子节点事件:getChildren的调用
移除监听器
我们可以调用removeWatches来移除一个注册在节点上的监听器。同样的,一个ZooKeeper客户端在没有服务器连接的情况下能移除本地的监听器,通过设置本地的标记为true。下面是事件的详细列表监听器成功的被移除后触发:
子节点移除事件:调用getChildren增加的监听器。
数据移除事件:调用exists或getData增加的监听器。
ZooKeeper对监听器的保证
对于监听器,ZooKeeper有下列的保障:
监听器和另外的事件,另外的监听器和异步的回复是有序的。ZooKeeper 客户端库确保每件事都有序分发。
一个客户端看到这个节点的新的数据之前,会先看到他监听的节点的一个监听事件。
从ZooKeeper 来的监听事件的顺序对应于ZooKeeper 服务看到的更新的顺序。
关于监听器要记住的事情
监听器是一次触发的,如果你得到了一个监听事件并且想继续得到未来的事件通知,你必须设置一个另外的监听器。
因为监听器是一次触发的,就会在得到事件和发送请求设置新的监听器之间有一个延迟,你不能看到ZooKeeper的节点上每次 改变。准备好处理在得到事件和设置监听器之间节点多次改变的情况(你或许不太关心,但至少要意识这会发生)。
一个监听器对象或一个函数/上下文对,为一个事件只会被触发一次。比如说,如果相同的监听器在一次exists或getData调用中被注册到了相同的文件,并且文件被删除,对于该文件删除的通知,监听器对象只会被调用一次。
当你从服务器断开连接,在恢复连接之前,你不会得到任何监听器。由于这个原因,会话事件会被发送给所有的未处理的监听器。使用会话事件进入一个安全模式:在断开期间,你不会收到事件,所以你的进程在这种模式下应该小心行事。
ZooKeeper使用ACLs控制访问
ZooKeeper使用ACLs来控制访问它的节点(ZooKeeper数据树上的数据节点)。ACL的实现和UNIX文件访问权限非常相似:它使用权限位来允许/拒绝对节点和位适用范围的各种操作。不像标准的UNIX权限,一个ZooKeeper节点没有限制在这三个标准的范围:user (文件拥有者)、group、world 。ZooKeeper没有节点拥有者的概念,取而代之的是,一个ACL指定ids和id相关的权限的集合。
还请注意一个ACL只适用于一个指定的节点,它也不适用于子节点。比如说,如果 /app节点只能被ip:172.16.16.1读取, /app/status是全部可读的,任何人都 可以读取/app/status。ACLs不是递归的。
ZooKeeper支持可插拔式的认证方案。Ids指定使用这个形式scheme:id,scheme是id对应的授权方案,比如说,ip:172.16.16.1是一个主机地址为172.16.16.1的id。
当一个客户端连接ZooKeeper并进行认证,ZooKeeper把符合这个客户端的所有ids联系起来。当客户端尝试存取一个节点的时候,这些ids用来检查一个节点的ACLs。ACLs由成对(scheme:expression, perms)的组成。expression的格式指定了权限,比如说,(ip:19.22.0.0/16, READ)给所有的以19.22开头的IP地址的客户端读的权限。
ZooKeeper支持下列权限:
CREATE:可以创建一个子节点
READ:可以从一个节点读取数据并展示子节点
WRITE:可以设置一个节点的数据
DELETE:可以删除一个子节点
ADMIN:可以设置权限
简介
对于想要利用ZooKeeper的协调服务来创建一个分布式应用的开发人员来说,这篇文章提供了指导。包含了一些概念和实际性操作的信息。
这篇文章的前四个章节介绍了各种ZooKeeper的概念,这对理解ZooKeeper是怎么工作的是必须的。没有包含源代码,但是它假设你对分布式处理有关的问题比较熟悉。这四个章节是:
ZooKeeper数据模型
ZooKeeper 会话
ZooKeeper Watches
一致性保证
随后的四个章节提供了实际的编程信息,他们是:
构建块:ZooKeeper操作指南
绑定
程序结构,简单的例子
陷阱:常见问题和故障排查
ZooKeeper数据模型
ZooKeeper有一个层级命名空间,和一个分布式文件系统非常相似。唯一的不同是每个节点可以有关联的数据,子节点也是。就像有一个文件系统并且允许文件是一个目录。一个规范的、绝对的、斜杠分隔的路径来表示一个节点路径,没有相对路径。任何符合下列约束的的unicode字符可以被使用:
null字符串(\u0000)不能是一个路径名称。
下列字符不能被使用,因为不能很好的被展示:\u0001 – \u001F 和 \u007F – \u009F。
下列字符是不允许的:\ud800 – uF8FF, \uFFF0 – uFFFF。
“.”字符可以作为另一个名字被使用,但是“.”和“..”不能单独使用来表示一个节点路径,因为ZooKeeper不使用相对路径,下列是无效的:”/a/b/./c”或者 “/a/b/../c”。
“zookeeper”标记被保留。
ZNodes
在ZooKeeper树中的每个节点被称为一个znode。Znodes包含了一个stat数据结构,这个数据结构包括了数据变更的版本号、acl变更。stat数据结构也有时间戳,版本号和时间戳一起来允许ZooKeeper校验缓存和协调更新。每当一个znode的数据改变,版本号就会增加。例如:当一个客户端取得数据,它同样也接受数据的版本。并且,当一个客户端执行一个更新或删除操作,它必须提供数据的版本号。如果客户端提供的的版本号和实际的版本号不匹配,更新操作将会失败。
注意 在分布式应用应用中,<em>node</em>一词可以用来表示一台主机、一个服务器、集中中的一个、一个客户端进程等等。早ZooKeeper这边文档中,<em>znodes </em>表示一个数据节点,<em>Servers</em>表示组成ZooKeeper服务中的机器,<em>quorum peers</em> 表示组成集合的机器,客户端表示使用一个ZooKeeper服务的主机或进程。
Znodes是一个程序员访问的主要实体,有许多在这里值得提到特性:
Watches
客户端可以在znodes上设置监听器,znode的改变触发这个监听器然后清空这个监听器。当一个监听器被触发,ZooKeeper发送给客户端一个通知。更多信息可以查看ZooKeeper Watches章节。
数据访问
每个znode的上存储的数据读写都是原子的,读操作取出所有的和这个znode有关的所有数据,写操作替换所有的数据。每个节点有一个访问权限列表(ACL)来限制谁可以做这些事情。
ZooKeeper没有被设计成一个一般的数据库或一个大型对象存储。它管理协调数据,数据可以是配置、状态信息、集合点等的形式。各种各样的数据有一个共同的属性就是他们都很小:以千字节为标准。ZooKeeper客户端和服务器有一个健康检查来确保znodes的数据少于1M,但是数据平均应该更小。操作较大的数据将导致一些操作花费更多的时间,并且会影响一些操作的延迟,因为在网络和存储媒介中移动更多的数据将需要额外的时间。如果需要存储大数据,通常的处理是把数据存储在一个大容量存储系统中,并把存储位置的指针存储到ZooKeeper上。
临时节点
ZooKeeper也临时节点的概念。这些znodes存活的时间和创建这个节点的会话有效期是一样的。当会话结束,节点被删除。因为这种临时节点的特性,临时节点不允许有子节点。
顺序节点——唯一名称
当创建一个节点的时候,也可以请求ZooKeeper在路径后面增加一个自增的计数器。对父节点来说,这个计数器是 唯一的。计数器是%010d的格式——是一个十位数,比如:<path>0000000001。
查看Queue Recipe使用这个特性的示例,注意:这个计数器用来存储下一个序列号是一个4字节的数,当增加到2147483647 之后,计数器会溢出。
ZooKeeper中的时间
ZooKeeper以多种方式跟踪时间:
Zxid:ZooKeeper状态的每次变化都接收一个zxid(ZooKeeper事务id)形式的标记。这个展示了所有的ZooKeeper的变更顺序。每次变更会有一个唯一的zxid,如果zxid1小于zxid2说明zxid1在zxid2之前发生。
Version numbers:节点的每次变化都会引起这个节点版本号之一的一次增加。这三个版本号是:version(一个节点的数据变化次数),cversion(一个节点的子节点变化次数),aversion(一个节点的ACL 变化次数)。
Tricks:当使用多个ZooKeeper服务,服务器使用ticks来确定事件的时间,比如说状态上传、会话超时、连接超时等。这个tick时间仅仅通过最小会话超时时间间接的暴露出来;如果一个客户端请求会话的超时时间小于最小超时时间,服务器将会告诉客户端实际的会话超时时间是最小超时时间。
Real Time:ZooKeeper不使用实时、时钟时间。除了把时间戳放在stat结构中。
ZooKeeper Stat 结构
每个节点的Stat结构由下列字段组成:
czxid:该数据节点被创建时的事务id。
mzxid:该节点最后一次被更新时的事务id。
ctime:节点被创建时的时间。
mtime:节点最后一次被更新时的时间。
version:这个节点的数据变化的次数。
cversion:这个节点的子节点 变化次数。
aversion:这个节点的ACL变化次数。
ephemeralOwner:如果这个节点是临时节点,表示创建者的会话id。如果不是临时节点,这个值是0。
dataLength:这个节点的数据长度。
numChildren:这个节点的子节点个数。
ZooKeeper会话
通过使用一种语言绑定来创建服务端的句柄,一个ZooKeeper客户端可以和ZooKeeper服务创建会话。一旦创建,句柄开始在CONNECTING 状态,客户端库尝试连接组成ZooKeeper服务中的其中一个服务器并且切换到CONNECTED状态。在正常的操作期间将会是这两种状态之一。如果一个不可恢复的错误发生了,比如说会话过期或授权失败,或者如果应用显示地关闭了句柄,句柄将会到CLOSED状态。下面的图展示了一个ZooKeeper客户端可能的状态转变。
为了创建一个客户端会话,应用程序代码必须提供一个连接字符串列表以逗号分隔开,主机:端口号成对出现,每个都相当于一个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”后缀可以被加在连接字符串后面,这会运行客户端命令导致所有的路径都和这个跟路径相关。如果使用像下面的示例:”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”作为跟路径,并且所有的路径都与这个根路径相关,比如getting、setting等。”/foo/bar” 将导致操作在”/app/a/foo/bar”(从服务端的观点来看)。这个特性在多租户下面是非常也有用的,ZooKeeper服务的每个用户可以有不同的根路径。这让再使用变得非常简单,因为每个用户都可以编写代码让他的应用好像在”/”根路径下,但实际的位置能在部署时决定。
当一个客户端从ZooKeeper服务得到一个句柄,ZooKeeper创建了一个会话,表现为一个64位的数字,并把 它分配给客户端。如果客户端连接到一个不同的服务端,在连接握手的时候它将发送这个会话id。作为一个安全措施,服务端给会话id创建了一个密码,让服务端能够校验。当客户端建立会话的时候,这个密码随着会话id一起发送给客户端。每当客户端与一个新的服务端恢复会话的时候,密码会随着会话id一起发送过去。
客户端调用创建会话的时候有一个参数是会话超时时间(毫秒),客户端发送一个要求的超时时间,服务端回复一个他能给客户端的超时时间。当前实现要求超时时间至少是2倍的tickTime,最大是20倍的tickTime。ZooKeeper客户端API允许使用一个协商的超时时间。
当一个客户端从ZK服务集群成为分区,它将开始寻找在会话创建时期指定的服务端列表。最终,当客户端和至少一个服务端联通重新建立的时候,会话要么转变成“connected”状态(如果在会话超时时间内恢复连接),要么转变成“expired”状态(如果在超时时间之外恢复连接)。在断开时创建一个新的会话是不可取的。ZK客户端库将处理连接。尤其是客户端内部有方法来处理像“羊群效应”之类的事情。仅仅在你被通知会话过期的时候去创建一个新的会话。
ZooKeeper集群自己管理会话过期,而不是由客户端管理。当ZK客户端和一个集群建立会话,它提供一个“超时时间”。这个值被集群使用来决定客户端的会话是否过期。当集群不能在指定的会话超时时间内从客户端收到信息,过期发生。在会话过期期间,集群将删除由这个会话创建的所有的临时节点,并且立即通知连接的客户端这个改变。此时,会话过期的客户端依然和集群式断开的,它不会收到通知直到它能和集群重新建立连接。这个客户端将保持断开状态直到和集群的TCP连接重新建立,并且在这个时候,过期会话的监听将会收到“会话过期”通知。
对于一个过期的会话,监听器所看到的状态转变:
“connected”:会话被建立,并且客户端能和集群交流
……客户端从集群被分割
“disconnected”:客户端与集群丢失了联系
……时间流逝,在超时时间之后,集群已经让这个会话过期,而客户端没看到什么,因为它已经从集群断开连接了
……时间流逝,客户端恢复网络和集群联通
“expired”:最后客户端与集群重新连接,然后收到过期的通知
ZooKeeper会话建立的另一个参数是默认监听器。当客户端的一些状态改变发生,监听器会收到通知。比如如果客户端丢失与服务端的连接,客户端将会收到通知,或客户端的会话到期等。这个监听器应该考虑初始状态到断开连接。对于一个新的连接,第一给发给监听器的事件就是会话连接事件。
客户端通过发送请求保持会话存活。如果会话在一段时间内空闲将会导致会话超时,客户端将会发送PING请求保持会话存活。这个PING请求不仅仅让ZooKeeper服务端知道客户端是存活的,而且让客户端检查它的和ZooKeeper 服务端的连接也是存活的。PING的时间是足够保守的合理时间,来发现死掉的连接和一个新的服务端重新连接。
一旦成功建立一个到服务端的连接,当客户端发生connectionloss异常 时有两种基本的情况,在执行一个同步或者非同步的操作时:
应用调用一个操作,但是会话不再存活。
当等待一个操作的时候ZooKeeper客户端从服务端断开连接,比如说:等待一个异步调用。
3.2.0新增——SessionMovedException。有一个内部的异常,通常不会被客户端发现,被称为SessionMovedException。一个已经连接的会话但是重新连接到了一个不同的服务器上接收了一个请求,这个异常就会发生。这个错误的正常原因是一个客户端发送了一个请求到一个服务端,但是网络数据包延迟了,所以客户端超时并连接到了一个新的服务器。当延迟的数据包到达了第一个服务器,这个服务端发现这个会话已经被移除了并且关闭了这个客户端连接。客户端一般不会发现这个错误因为它们不在从老的连接读取数据(老的连接一般被关闭了)。这种事情发生的另一种情况是当两个客户端使用一个保存的会话id和密码来尝试恢复相同的连接时,只有一个客户端能够恢复连接,另一个客户端将会断开。
更新服务器列表。我们允许一个客户端更新连接字符串通过提供一个新的逗号分隔的主机:端口号列表,每个都是一个服务器。函数调用一个概率负载均衡算法会引起客户端断开与当前主机的连接,来使在新列表中的每个服务器达到与预期一致的数量。万一客户端连接的当前主机不在新的列表中,这个调用会引起连接被删除。另外,这个决定基于是否服务器的数量增加或减少了多少。
比如说,如果之前的连接包含三个主机,现在的连接多了两个主机,连接到每个主机的客户端的40%为了负载均衡将会移动到新的主机上去。这个算法会引起客户端断掉它当前与服务器的连接,这个概览是0.4,并且客户端随机选择两个新主机中的一个连接。
另一个例子,假设我们有5个主机,然后现在更新列表移除两个主机,连接到剩余三台主机的客户端依然保持连接,然而所有连接到已被移除主机的客户端都需要移到剩下三台主机的一台上,并且这种选择是随机的。如果连接断开,客户端进入一个特殊的模式并使用概率算法选择一个新的服务器,而不仅仅只是循环。
在第一个例子中,每个客户端决定断开连接的概览为0.4,但是一旦做了决定,它将会随机的连接到一个新的服务器,仅仅当它不能连接到任何一台新的服务器上时,它将尝试连接旧的服务器。当找到一个服务器或者新列表中所有的服务器都连接失败的时候,客户端回到操作正常模式,选择一个任意的服务器并尝试连接它,如果连接失败,它会继续尝试不同的随机的服务器,并一直循环下去。
ZooKeeper Watches
ZooKeeper中所有的读操作——getData(), getChildren()和 exists()—可以选择设置 一个监听器。这是ZooKeeper’s一个监听器的定义:一个监听事件是一次性触发,当一个被设置监听的数据改变时,发送给设置这个监听器的客户端。在这个监听器的定义中,有三个要点:
一次性触发:当数据改变的时候一个监听事件会被发送给客户端。比如说,如果一个客户端做了getData(“/znode1″, true)操作,然后 /znode1下的数据被改变或者删除了,客户端将得到/znode1的一个监听事件。如果/znode1节点再次发生改变,没有监听事件会被发送除非客户端做了别的设置了一个新的监听器。
发送到客户端:这意味着事件正在发送给客户端的途中,但是在操作成功的返回码到达发起这个变更操作的客户端之前,事件可能还没到达监听的客户端。ZooKeeper提供了一个有序保证:在它第一次看到监听事件之前,它永远不会看到它设置的监听改变。网络延迟或别的因素可能会引起不同的客户端看见监听器和更新操作的返回码,在不同的时间。关键得一点是不同的客户端看见的每件事有一个一致的顺序。
被设置监听的数据:这是指一个节点能变化的不同方式。可以认为ZooKeeper有两个监听器列表:数据监听和子节点监听。getData()和exists()设置数据监听器。 getChildren()设置子节点监听器。二选一,根据返回数据的类型来设置监听器。getData()和exists()返回节点的数据信息,然而getChildren()返回一个子节点列表。因此,setData()会触发数据监听器。一个成功的 create()会触发一个数据监听器。一个delete()会触发数据监听器和子节点监听器。
在ZooKeeper服务器中,当客户端连接的时候,监听器被保存在本地。这使得监听器轻量级的被设置、保存、分发。当一个客户端连接一个新的服务器,监听器会触发一些会话事件。当从服务器断开连接的时候,不会受到监听器。当一个客户端重新连接,如果需要的话,之前注册的监听器会被注册和触发。有一个监听器可能丢失的情况:如果在断开连接期间,一个节点被创建和删除,一个已存在的节点的监听器还没有创建,将丢失。
监听器的意思
我们能在三种调用读取ZooKeeper状态的情况下设置监听器:exists,getData和getChildren,下面的列表是一个监听器触发的事件的详细情况:
创建事件:exists的调用
删除事件:exists,getData和getChildren的调用
改变事件:exists,getData的调用
子节点事件:getChildren的调用
移除监听器
我们可以调用removeWatches来移除一个注册在节点上的监听器。同样的,一个ZooKeeper客户端在没有服务器连接的情况下能移除本地的监听器,通过设置本地的标记为true。下面是事件的详细列表监听器成功的被移除后触发:
子节点移除事件:调用getChildren增加的监听器。
数据移除事件:调用exists或getData增加的监听器。
ZooKeeper对监听器的保证
对于监听器,ZooKeeper有下列的保障:
监听器和另外的事件,另外的监听器和异步的回复是有序的。ZooKeeper 客户端库确保每件事都有序分发。
一个客户端看到这个节点的新的数据之前,会先看到他监听的节点的一个监听事件。
从ZooKeeper 来的监听事件的顺序对应于ZooKeeper 服务看到的更新的顺序。
关于监听器要记住的事情
监听器是一次触发的,如果你得到了一个监听事件并且想继续得到未来的事件通知,你必须设置一个另外的监听器。
因为监听器是一次触发的,就会在得到事件和发送请求设置新的监听器之间有一个延迟,你不能看到ZooKeeper的节点上每次 改变。准备好处理在得到事件和设置监听器之间节点多次改变的情况(你或许不太关心,但至少要意识这会发生)。
一个监听器对象或一个函数/上下文对,为一个事件只会被触发一次。比如说,如果相同的监听器在一次exists或getData调用中被注册到了相同的文件,并且文件被删除,对于该文件删除的通知,监听器对象只会被调用一次。
当你从服务器断开连接,在恢复连接之前,你不会得到任何监听器。由于这个原因,会话事件会被发送给所有的未处理的监听器。使用会话事件进入一个安全模式:在断开期间,你不会收到事件,所以你的进程在这种模式下应该小心行事。
ZooKeeper使用ACLs控制访问
ZooKeeper使用ACLs来控制访问它的节点(ZooKeeper数据树上的数据节点)。ACL的实现和UNIX文件访问权限非常相似:它使用权限位来允许/拒绝对节点和位适用范围的各种操作。不像标准的UNIX权限,一个ZooKeeper节点没有限制在这三个标准的范围:user (文件拥有者)、group、world 。ZooKeeper没有节点拥有者的概念,取而代之的是,一个ACL指定ids和id相关的权限的集合。
还请注意一个ACL只适用于一个指定的节点,它也不适用于子节点。比如说,如果 /app节点只能被ip:172.16.16.1读取, /app/status是全部可读的,任何人都 可以读取/app/status。ACLs不是递归的。
ZooKeeper支持可插拔式的认证方案。Ids指定使用这个形式scheme:id,scheme是id对应的授权方案,比如说,ip:172.16.16.1是一个主机地址为172.16.16.1的id。
当一个客户端连接ZooKeeper并进行认证,ZooKeeper把符合这个客户端的所有ids联系起来。当客户端尝试存取一个节点的时候,这些ids用来检查一个节点的ACLs。ACLs由成对(scheme:expression, perms)的组成。expression的格式指定了权限,比如说,(ip:19.22.0.0/16, READ)给所有的以19.22开头的IP地址的客户端读的权限。
ZooKeeper支持下列权限:
CREATE:可以创建一个子节点
READ:可以从一个节点读取数据并展示子节点
WRITE:可以设置一个节点的数据
DELETE:可以删除一个子节点
ADMIN:可以设置权限
发表评论
-
大型网站架构系列:20本技术书籍推荐
2016-07-18 16:16 487http://www.cnblogs.com/itfly8/p ... -
大型网站架构系列:负载均衡详解
2016-07-18 16:16 398原文地址:http://www.cnblogs.com/itf ... -
一致性哈希算法
2016-07-17 10:02 314原文地址:http://www.blogjava.net/he ... -
Java虚拟机
2016-07-17 10:02 298JAVA虚拟机的生命周期:http://www.importn ... -
理解Java虚拟机体系结构
2016-07-17 10:05 365原文地址:http://www.impor ... -
Java并发编程:线程池的使用
2016-07-17 10:03 255原文地址:http://www.cnblogs.com/dol ... -
Java并发编程
2016-07-17 10:06 388Java并发编程(1):可重入内置锁 http://www.i ... -
8张图理解Java
2016-07-18 16:14 302原文地址:http://www.importnew.com/1 ... -
Java并发编程:Lock
2016-07-13 10:34 305原文地址:http://www.cnblo ... -
Java集合框架:HashMap
2016-07-13 10:15 358原文地址:http://www.importnew.com/1 ... -
HashMap的实现原理
2016-07-13 10:11 281原文地址:http://www.impor ... -
HashMap的工作原理
2016-07-13 10:08 252原文地址:http://www.impor ... -
Java线程面试题 Top 50
2016-07-13 09:58 249原文地址:http://www.impor ...
相关推荐
《ZooKeeper编程指导》是一本深度探讨ZooKeeper这一分布式协调服务的开发技术书籍。在9页的篇幅中,它可能涵盖了ZooKeeper的基础概念、核心原理以及实际应用。ZooKeeper作为Apache的一个开源项目,是为分布式应用...
总之,《ZooKeeper程序员指南》是一本全面介绍ZooKeeper使用方法的书籍,不仅对ZooKeeper的概念提供了深入分析,还提供了丰富的实践编程指导,是开发者创建基于ZooKeeper的分布式应用不可或缺的参考资料。
分布式ICE中间件是一种高效的远程过程调用(RPC)框架,它支持多种语言的跨语言开发,提供了一种让不同编程语言编写的软件之间能够互相通信的机制。ICE,即Internet Communication Engine,是一种面向对象的中间件,它...
编程方面,Zookeeper提供了丰富的API,允许开发者使用Java或C进行编程。例如,可以使用create()方法创建节点,exists()检查节点是否存在,get()和set()用于读写节点数据,以及watcher监听机制来响应数据变化。 总结...
此外,性能调优章节提供了关于垃圾回收、内存管理、压缩算法、合并策略等方面的深度见解,指导用户如何根据具体业务需求调整HBase的运行参数,以达到最佳的系统性能。 ### 监控与管理 在集群监控部分,介绍了HBase...
CAP原理(一致性、可用性和分区容忍性)指导我们设计高可用的分布式系统。Paxos和Raft算法是解决分布式一致性问题的常见方法。Zookeeper和etcd提供了基于这些算法的强一致性解决方案。 最后,Spring Cloud和Dubbo是...
本指南将指导用户从头开始搭建 Hadoop 环境,包括虚拟机环境的准备、Linux 基础知识、shell 增强大数据集群环境准备、ZooKeeper 介绍及集群操作网络编程等方面的内容。 虚拟机环境准备 虚拟机环境准备是搭建 ...
1. HBase安装与配置:遵循指导步骤,完成HBase的伪分布式部署,确保所有服务正常运行,可以通过Web UI(localhost:16010)查看状态。 2. HBase操作实践:创建名为“person”的表,然后执行一系列操作,如列出所有表...
7. 文档还可能涉及Java在处理并发、网络编程以及分布式系统架构设计方面的知识,这是面试者在面试过程中需要准备的内容。 8. 面试者还可能需要了解如何在Java中高效地使用Zookeeper客户端API进行开发,包括如何创建...
解压后,开发者可以查看源代码,了解其实现细节,也可以通过安装指导进行项目集成。 总的来说,"Pythonic-0.12.tar.gz"是一个在PyPI上的Python库,它可能提供了与ZooKeeper的集成,支持分布式环境,并遵循云原生的...
为了充分利用这个库,开发者需要了解ZooKeeper的基本概念以及Python编程,同时也应具备一定的分布式系统和云环境的知识。对于初次使用者,阅读解压后的README文件将是开始使用的关键步骤,它会指导如何安装和配置...
Java方向详解版技术指导手册 Java概述 Java是一种高级的编程语言,具有跨平台、面向对象、简单易学、安全可靠等特点。Java技术平台主要包括JavaSE(Standard Edition)、JavaEE(Enterprise Edition)和JavaME...
为了充分利用certgrinderd,开发者需要熟悉Python编程语言,理解Zookeeper的工作原理和API,以及如何在Python环境中安装和导入第三方库。此外,由于certgrinderd可能涉及到分布式系统的设计和实现,对分布式系统的...
为了充分利用`clihandler`,开发者需要对Python编程、ZooKeeper的工作原理以及云原生架构有一定的了解。他们可以通过阅读`clihandler`的文档(如果提供),理解其API接口,学习如何在项目中集成和调用这个库。此外,...
4. **Python库**:显然,`toolchest-client`是一个Python编程语言的库,可以被其他Python程序导入和使用,提供了对Zookeeper和其他分布式功能的Python接口。 从压缩包子文件的文件名称列表`toolchest-client-0.6.0`...
这可能包括了安装Java环境、配置Apache服务器、导入必要的Java项目、设置ZooKeeper集群以及确保高可用性和数据一致性等方面的指导。 对于Java_Apache管理员来说,掌握这些知识是基础,他们需要能够有效地监控和管理...