zookeeper学习笔记

上一篇 / 下一篇  2011-09-30 18:08:22 / 个人分类:分布式技术学习

 zookeeper的原语操作是一组丰富的“构件”,可用于实现很多协调数据结构和协议。相关的例子包括:分布式队列,分布式锁和一组同级节点中的”领导者选举“,zookeeper采用松耦合交互方式,一个进程可以在zooleeper中留下一条信息,在该进程结束后,另外一个进程还可以读取这条信息。
配置文件:zoo.cfg在conf下。如果设置了环境变量ZOOCFGDIR,也可以保存在该环境变量所指定的目录中。dataDir属性指定了zookeeper存储持久数据的本地文件系统的位置。
使用nc发送ruok到监听端口检查zookeeper是否正在运行。echo ruok | nc localhost 2181。
常用四个字母命令:
dump命令用于列出会话和”短暂znode“;envi命令用于列出服务器属性;reqs命令用于列出未完成的请求;stat命令用于列出服务统计信息和连接的客户端。srst命令用于重置服务统计信息;从运行zookeeper服务器的主机上发出kill命令可以关闭zookeeper。在zookeeper没有文件和目录,而是统一使用”节点的概念成为znode“
zookeeper服务:zookeeper维护着一个树形层次结构,树中的节点被成为znode。znode可以用于存储数据,并有着一个与之相关联的ACL。zookepper被设计用来协调服务,而不是用于大容量数据存储。
zookeeper的数据访问具有原子性。读写要么成功要么失败,不存在部分的情况。zookeeper不支持添加操作,这些特征都是与hdfs不同的。znode通过路径被引用。与unix不同的是zookeeper的路径必须是绝对路径。路径只有一种表示方式。字符串zookeeper是保留词,不能将他用作一个路径组件。
znode有两种类型:短暂和持久的。znode的类型在创建时确定并且之后不能再修改。
在创建短暂znode的客户端会话结束时,zookeeper会将短暂znode删除。
持久znode不依赖客户端会话,只有当客户端(不一定是创建它的客户端)明确要删除该持久znode时才会被删除。
短暂znode不可以有子节点,即使是短暂字节点。
对于那些需要知道特定时刻有哪些分布式资源可用的应用来说,使用短暂znode是一种理想的选择。
顺序号znode是指名称中包含zookeeper指定顺序号的znode。如果在创建znode时设置了顺序标识,那么该znode名称之后便会附加一个值,这个值是由一个单调递增的计数器(由父节点维护)所添加的。在java的api中,顺序znode的实际路径会作为create()调用的返回值被传回客户端。
在一个分布式系统中,顺序号可以被用于为所有事件进行全局排序,这样客户端就可以通过顺序号来推断事情的顺序。
znode以某种方式发生变化时,watch机制可以让客户端得到通知。
zookeeper服务的操作:
create:创建一个znode(必须要有父节点)
delete:删除一个znode(该znode不能有任何子节点)
exists:测试一个znode是否存在并且查询他的元数据
getACL,setACL:获取/设置一个znode的ACL(ACL代表应用对该节点的访问权限)
getChildren:获取一个znode的子节点列表。
getData,setData:获取/设置一个znode所保存的数据。
sync:将客户端的znode视图与zookeeper同步。
zookeeper中的更新操作是有条件的,在使用delete或setdata操作时必须提供被更新的znode的版本号(可以通过exists操作获得)。如果版本好不匹配,更新失败。更新操作是非阻塞操作。
zookeeper允许读到的数据滞后于zookeeper服务的最新状态。
在读操作exists,getchildren和getdata上可以设置观察,这些观察可以被写操作create,delete。setData触发。ACL相关的操作不参与任何观察。当一个观察被触发时会产生一个观察事件,这个观察和触发它的操作共同决定了观察时事件的类型。
一个观察事件中包含涉及事件的znode的路径,因此对于nodecreate和nodedeleted事件来说,可通过路径来判断哪一个节点被创建或删除。
ACL:每个znode被创建时都会带有一个ACL列表,用于决定谁可以对它执行何种操作。
ACL依赖于zookeeper的客户端身份验证机制。zookeeper提供了下面几种身份验证模式。
digest:通过用户名和密码来识别客户端。
host:通过客户端的主机名来识别客户端。
ip:通过客户端的主机名来识别客户端。
每个ACL都是身份验证模式,符合该模式的一个身份和一组权限的组合。exists不受ACL的控制。
zookeeper服务有两种不同的运行模式。
一种是“独立模式”,即只有一个zookeeper服务器。
一种是“复制模式”运行于一个计算机集群上。只要集合体中半数以上的机器处于可用状态,它就能提供服务。所以通常一个集合体通常包含奇数台机器。
从概念上说,zookeeper是:确保对znode树的每一个修改都会被复制到集合体中超过半数的机器上。如果少于半数的机器出现故障,则至少有一台机器会保存最新的状态。其余的副本最终也更新到这个状态。
所走的阶段:
1:领导者选举:集合体中的所有机器通过一个选择过程来选出一台被称为“领导者”的机器,其他的机器被称为“跟随者”。一旦半数以上的跟随者几经将其状态与领导者同步,则表明这个阶段已经完成。
2:原子广播:所有的写请求都被转发给领导者,再由领导者将更新广播给跟随者。当半数以上的跟随者已经将修改持久化之后,领导者才会提交这个更新,然后客户端才会收到一个更新成功的响应。这个用来达成共识的协议被设计成具有原子性,因此每个修改要么成功要么失败。
如果领导者出现故障,其余的机器会选出另外一个领导者,并和新的领导者一起继续提供服务。随后,如果之前的领导者恢复正常,变成为一个跟随者。领导者的选举过程是非常快的,200毫秒左右,不会出现性能的明显降低。
在更新内存中的znode之前,集合体中的所有机器都会先将更新写入磁盘。任何一台机器都可以为读请求提供服务,读请求只涉及内存检索,因此非常快。
一致性:一个跟随者可能滞后于领导者几个更新。在一个修改被提交前,只需要集合体中半数以上而非全部机器已经将其持久化。每个客户端都可能被连接到领导者,但客户端对此无法控制(但是对zookeeper可以进行配置,使领导者不受任何客户端的连接)。每一个对znode树的更新都被赋予一个全局唯一的ID,称为zxid。zookeeper决定了分布式系统中的顺序,它对所有更新进行排序,如果z1小于z2,那么z1一定发生在z2之前。
 
跟随者响应读请求,领导者提交请求。
zookeeper如何保证数据一致性:
1.顺序一致性:来自任意特定客户端的更新都会按其发送顺序被提交。
2.原子性:更新要么成功要么失败。
3.单一系统映像:一个客户端无论连接到哪一台服务器,它看到的都是同样的系统视图。如果一个客户端在同一个会话中连接到一台新的服务器,它所看到的系统状态不会比之前服务器上看到的更老。当一台服务其出现故障导致它的一个客户端需要尝试连接集合体中其他服务器时,所有滞后于故障服务器的服务器都不会接受该连接请求。
4.持久性:一旦更新成功不会再被撤消。
5.及时性:任何客户端看到的系统视图滞后都是有限的。
由于性能原因,所有的读操作都是从zookeeper服务器的内存获得数据,它们不参与写操作的全局排序。如果客户端之间通过zookeeper之外的机制进行通信,则这个性质可能会导致客户端所看到的zookeeper状态是不一致的。
例如,客户端A将znode的z值更改又告诉B去读z的值,那么读到的就是更新前的值。为了避免这种情况,B在读之前对z调用sync操作。sync强制B所连的zookeeper服务器赶上领导者。只能以异步调用的方式来使用sync。
只要一个会话空闲超过一定时间都可以通过客户端发送ping请求(心跳)保持会话不过期,这个长度应该设置足够低以便能检测出服务器故障(由读超时体现并且能够在会话超时的时间段内重新连接到另外一台服务器)。
较短的会话超时设置会较快地检测到机器故障。但是有时繁忙的网络会导致数据包传输延迟,从而可能会无意中导致会话过期,这种情况下可能会出现振动现象:在短时间内反复离开而后重新加入。
 
服务器会为每个会话分配一个唯一的ID和密码,如果在创建连接的过程中将它们传递给zookeeper,可以用于恢复一个会话(只要这个会话没过期)。
状态:
可以通过getState()方法来查询对象的状态。一个zookeeper的实例在一个时刻只能处于一种状态。
zookeeper的观察对象肩负着双重责任:一方面用来获得zookeeper状态变化的相关通知(connecting,connected等)。另一方面还可以用来获得znode变化的相关通知。监视znode变化可以使用一个专用的观察对象,也可以通过读操作中的布尔标识来设定是否使用默认的观察。
 
interuppted异常:这个异常并不意味着有故障,而是表明响应的操作已经被取消,所以在配置服务的示例中,可以通过传播异常来中止应用程序的运行。
keeperException异常:如果zookeeper服务器发出一个错误信号或与服务器存在通信问题,抛出的则是keeperException异常。针对不同的错误情况,zookeeperException存在不同的子类。eg:keeperException.NoNodeException,如果试图针对一个不存在的znode执行操作,抛出的则是该异常。
状态异常:
当一个操作因不能被应用于znode树而导致失败时,就会出现状态异常。状态异常产生的原因通常是在同一时间有另外一个进程正在修改znode。keeperException.BadVersionException异常,是因为版本号不匹配。如果一个znode先被另外一个进程更新了,根据版本号进行setData操作的进程就会失败。keeperException.NoChildrenForEphemeralException异常,试图在短暂znode下创建字节点时就会抛出该异常。
可恢复的异常:
 
可恢复的异常是指那些应用程序能够在同一个zookeeper会话中恢复的异常。一个可恢复的异常通常是通过zookeeperException.ConnectionLossException异常,相关的操作是否成功执行。这种情况就是失败的一个例子。这一点上,就需要对“幂等”操作和“非幂等”操作进行区分。幂等操作是指那些一次或者多次执行都会产生相同结果的操作。对于幂等操作,只要简单地进行重试即可。
对于非幂等操作,就不能盲目地进行重试,因为它们多次执行的结果与一次执行是完全不同的。程序可以通过znode的路径和它的数据中编码信息来检测是否非幂等操作的更新已完成。
不可恢复的异常:
某些情况下,会话会失效--也行因为超时或因为会话被关闭(两种情况下都会受到keeperException.sessionExpiredException异常),或者身份验证失败。
锁服务:
分布式锁在一组进程之间提供了一种互斥机制。在任何时刻,只有一个进程可以持有锁。分布式锁可以用于在大型分布式系统中实现领导着选举,在任何时间点,持有锁的那个进程就是系统领导者。为了使用zookeeper znode来实现分布式锁服务,我们使用顺序znode来为那些竞争锁的进程强制排序。顺序号最小的持有锁。
zookeeper中有一个“观察节点”的概念,指没有投票权的跟随者。由于观察节点不参与写请求过程中达成共识的投票,因此使用观察节点可以让zookeeper集群在不影响写性能的情况下提高读操作的性能。
zookeeper配置:
 
clientPort:2181用于客户端连接。
server.1=zookeeper1:2888:3888   对于领导者来说2888端口用于跟随着连接。3888用于领导者选举阶段的其他服务器连接。
一个zookeeper服务器启动时,它读取myid文件用于确定自己的服务器id,然后通过读取配置文件来确定应当在哪个端口进行监听,同时确定集合体中其他服务器的网络地址。
在复制模式下,有两个额外强制参数:initLimit和syncLimit,两者都是以滴答参数的倍数进行度量的。
initLimit:设定允许所有跟随者与领导者进行连接并同步的时间。如果在设定时间段内,半数以上的跟随者未能完成同步,领导者便会宣布放弃领导地位,然后进行另外以为领导者的选举。如果这种情况经常发生(可通过日志查看),则表明这个值设定的太小。
syncLimit:允许一个跟随者与领导者进行同步的时间。如果在设定的时间段内,一个跟随者未能完成同步,他会自己重启。所有关联到这个跟随者的客户端将连接到另外一个跟随者。


TAG:

 

评分:0

我来说两句

Open Toolbar