八、可恢复故障
一、连接丢失
连接丢失的情况下,客户端提交一个create,同步请求,会得到ConnectionException异常,异步请求会得到CONNECTIONLOSS返回码,然而客户端无法通过异常和返回码来判断请求是否已经被处理。
客户端重启? 30个客户端重启?
解决方案:
开发者可以很容易实现关闭连接句柄
如果由于Zookeeper集群停机造成,等待回复。进程挂起,不用动。
二、已存在监视点与Disconnected事件
客户端监视/event创建事件,然而就在/event被另一个客户端创建时,c2与zk失去连接,这段时间里,其他客户端删除了/event,当c2和zk重连并注册监视点时,C2已经错过了create事件
解决方案:
尽量避免监视一个znode的创建事件
如果一定要监视创建事件,尽量监视存活期更长的znode的创建(不会轻易被删除)
九、不可恢复故障
当客户端无法提供适当的认证信息来完成会话的认证时,或Disconnected事件后客户端重新连接到已过期的会话,就会发生不可恢复的故障
客户端直到T4的时刻,才知道自己已经失去连接。
处理不可恢复故障最简单的方法是终止进程并重启,通过一个新的会话重新初始化自己的状态
十、请求、事务和标识符
那些改变zk状态的客户端请求(create、delete、setData)将会被转发给群首,群首执行响应的请求,并形成状态的更新,我们称为事务。在zookeeper中并不存在传统数据库存在的回滚机制,而是确保每一步操作都互不干扰。当群首产生一个事务,就会给该事务分配一个标识符,我们称之为zk会话id(zxid),zxid为一个64位的整数
低32位用作简单计数器。高32位是一个epoch。每当新Leader接管它时,将获取日志中Zxid最大的epoch,新Leader Zxid的epoch位设置为epoch+1,counter位设置0。用epoch来标记领导关系的改变,并要求Quorum Servers 通过epoch来识别该leader,避免了多个Leader用同一个Zxid发布不同的提议。
十一、群首选举
设置群首的目的是为了对客户端发起的zk状态变更请求进行排序。
服务器有几种状态,looking、leadering、following
处于looking状态就会向集群中每个服务器发送一个通知消息,该消息包括服务器的投票信息,投票中包括服务器标识(sid)和最近执行的事务的zxid信息。比如,一个服务器所发送的投票信息为(1,5),表示该服务器的sid为1,最近执行的事务的zxid为5.如果多个服务器拥有同样的zxid,那么sid大的将赢得选举。
但是投票由于网络延迟等,可能造成完全不同的结果
如果该群首没有足够多的追随者,将导致重新选主
十二、ZAB协议
ZAB:原子状态广播协议
在接收到一个写请求,追随者会将请求转给群首,群首将探索性地执行该请求,并将执行结果以事务的方式对状态更新进行广播。当事务提交时,服务器就会将这些变更反馈到数据树上。
提交过程类似于一个两段式提交
群首向所有追随者发送一个poposal消息p
当一个追随者接受到消息p后,会响应群首一个ack,通知群首已经收到该提案
当收到仲裁数量的服务器发送的确认消息后,群首就会向追随者发送commit操作
ZAB的两种模式:
- 崩溃恢复:当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB 协议就会进人恢复模式并选举产生新的Leader服务器。当选举产生了新的 Leader 服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后,ZAB协议就会退出恢复模式。其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致。
- 消息广播:当集群中已经有过半的Follower服务器完成了和Leader服务器的状态同步,那么整个服务框架就可以进人消息广播模式了。 当一台同样遵守ZAB协议的服务器启动后加人到集群中时,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么新加人的服务器就会自觉地进人数据恢复模式:找到Leader所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。正如上文介绍中所说的,ZooKeeper设计成只允许唯一的一个Leader服务器来进行事务请求的处理。Leader服务器在接收到客户端的事务请求后,会生成对应的事务提案并发起一轮广播协议;而如果集群中的其他机器接收到客户端的事务请求,那么这些非Leader服务器会首先将这个事务请求转发给Leader服务器。
十三、ZK服务处理流程
服务端角色包括:leader、follower和observer
Leader:
Leader处理流程如下:
1)PrepRequestProcessor:接收请求并将其封装为一个事务;
2)ProposalRequestProcessor:将事务封装为proposal,并广播给follower;ProposalRequestProcessor会前转所有的请求给CommitRequestProcessor,并且转发所有写请求到SyncRequestProcessor;
3)SyncRequestProcessor:持久化事务,并将消息交由AckRequestProcessor处理;
4)AckRequestProcessor:产生一个确认并转交给自己;
5)CommitRequestProcessor:等待收到足够的确认(超过一半)后,提交proposal;
6)ToBeAppliedRequestProcessor:获取等待处理的请求,并交给FinalRequestProcessor处理;
7)FinalRequestProcessor:执行请求,包括写请求和读请求。
在持久化事务时,ZooKeeper为了加快持久化的效率,使用了:
1)一次存储多个事务,减少磁盘I/O;
2)为文件预分配磁盘块。 ZK为文件预分配了相当大的块来避免每次写文件带来的元数据的管理开销。
follower:
follower需要接收3中不同的消息:客户端请求、proposal和commit。
1)FollowerRequestProcessor:接收和处理客户端请求,前转所有请求到CommitRequestProcessor,并前转写请求到leader;
2)CommitRequestProcessor:直接将读请求交由FinalRequestProcessor处理,而对于写请求,等待leader的commit通知;当收到commit通知后,将写请求交给
3)SyncRequestProcessor:接收来自leader的proposal,持久化事务,并将其交给SendAckRequestProcessor;
4)SendAckRequestProcessor:向leader发送确认消息;
5)FinalRequestProcessor:执行请求,包括写请求和读请求。
Observer:
observer的处理方式和follower是类似的,observer不参与确认proposal,因此比起follower,缺少了发送确认消息到leader和持久化事务的流程。
请求的按序处理ZooKeeper服务端需要保证请求的按序处理,在ZooKeeper的服务端收到一个请求后,会将其放到一个队列中,包括读请求和写请求,然后按序处理。对于读请求,当前服务端直接处理并返回结果(参考上面介绍的服务端流程);而对于写请求,当前服务端会等待直到该写请求处理完成,才继续处理下一个请求,通过该等待机制,ZooKeeper保证了请求的按序处理。
十四、文件元数据
任何文件系统中的数据分为数据和元数据。数据是指普通文件中的实际数据,即文件的实际内容;而元数据指用来描述一个文件的特征的系统数据,诸如访问权限、文件拥有者以及文件数据块的分布信息(inode…)等等。在集群文件系统中,分布信息包括文件在磁盘上的位置以及磁盘在集群中的位置。用户需要操作一个文件必须首先得到它的元数据,才能定位到文件的位置并且得到文件的内容或相关属性。
文件的元数据有以下几项内容:
在linux系统下,使用stat命令可以查看一个文件的上述信息
十五、本地存储
日志和磁盘的使用
服务器通过事务日志来持久化事务,在接受一个提议时,一个服务器就会将提议的事务持久化到事务日志中,该事务日志保存在服务器的本地磁盘中,而事务将会按照顺序追加其后。
注意:服务器只有在强制将事务写入事务日志之后才确认对应的提议。更准确的说,服务器调用ZKDatabase的commit方法,这个方法最终会调用FileChannel.force。这样,(写缓存关闭的情况下会持久化到磁盘,磁盘写缓存开启,force可能写缓存)
概念:
组提交:是指一次磁盘写入时追加多个事务,持久化多个事务只需要一次磁道寻址开销。
补白(padding):文件中预分配磁盘存储块。这样做,对于设计存储块分配的文件系统元数据的更新,就不会显著影响文件的顺序写入。假如需要高速向日志中追加事务,而文件没有原先分配存储块,那么文件系统需要分配一个新存储块。而通过补白可以减少两次额外的磁盘寻址开销:一次是更新元数据,一次是返回文件。(更新元数据:哪块空闲,返回文件:空闲块的首地址)
为了避免受到系统中其他写操作的干扰,我们强烈推荐将事务日志写入到一个独立的磁盘,将第二块磁盘用于操作系统文件和快照。
快照:
快照是zk数据树的拷贝副本,每个服务器经常序列化整个数据树来提取快照,并保存到文件。进行快照时,正常处理其他请求。如果这个期间有其他的请求后和快照不一致,那么这个快照称作模糊的快照,模糊快照最后一个事务的时间戳记为(TS),ZK在使用快照恢复到TS后,会使用事务日志,进行重放日志TS-now。
序列化协议:网络传输,磁盘保存的序列化消息和事务,Jute,目的是与老版本兼容,性能不如protobuf。
服务器类型:独立服务器,群首服务器,追随者服务器,观察者服务器(不参与投票)
十六、监控和运维
JMX用jconsole连上,观测变量指标,和四字命令指标类似
四字命令:
stat 查看状态信息,服务器是群首还是追随者,该服务器最后通知的zxid。
ruok 查看zookeeper是否启动
dump 列出没有处理的节点,临时节点
conf 查看服务器配置
cons 显示连接到服务端的信息
envi 显示环境变量信息
mntr 查看zk的健康信息
wchs 展示watch的信息
wchc和wchp 显示session的watch信息 path的watch信息
十七、配置参数
十八、Zookeeper常见问题
1、Paxos和ZAB区别?
ZAB, Paxos两者的联系:
两者都存在类似leader的角色,由其负责协调多个follower的工作
leader进程都会等待超过半数的follower做出正确的反馈后,才会将一个提案进行提交
在ZAB协议中,每个proposal都包含一个epoch值,用来代表当前leader周期, Paxos算法中,同样存在这样的标识,只是名字为Ballot
Paxos算法一个leader选举过程中,会进行2个阶段的操作,
第一阶段为读阶段,新的主进程会通过所有其他进程进行通信的方式来收集上一个主进程提出的提案,并将提案提交
第二阶段为写阶段,主进程提出自己的提案.
ZAB协议分为3个阶段
第一阶段, 类似paxos读阶段, zab中为发现阶段,
第二阶段,同步阶段,新的leader会确保在过半follower已经提交了之前leader周期中所有事务proposal,同步阶段可以保证leader在新的周期提出事务proposal之前, 所有进程均已经完成了对之前事务proposal的提交.
第三阶段,同步阶段完成之后, 类似paxos的写阶段执行.
总结:
ZAB协议和paxos协议本质区别在于:
ZAB协议主要用于构建一个高可用的分布式数据主备系统,改造paxos,利用zk的特性,实现主备数据同步的顺序性。(保障顺序性,Paxos不保证)
Paxos算法用于构建一个分布式的一致性状态机系统,(对一致性保证更好,ZAB为精简版)
2、用zk实现一个分布式队列,当一个队列成员聚齐,队列才可用,否则一直等待所有成员到达
在约定目录下创建临时目录节点,监听节点数据是否是我们想要的数目
用zk实现队列的入队出队操作
答:在指定目录创建PERSISTENT_SEQUENTIAL,监听目录,数据产生即为入队。Watch触发即为peek(),删除序号最小的节点,用于出队。
3、Paxos算法的应用
1.database replication, log replication等, 如bdb(Berkeley)的数据复制就是使用paxos兼容的算法。Paxos最大的用途就是保持多个节点数据的一致性。
2。naming service, 如大型系统内部通常存在多个接口服务相互调用。
- 通常的实现是将服务的ip/hostname写死在配置中,当service发生故障时候,通过手工更改配置文件或者修改DNS指向的方法来解决。缺点是可维护性差,内部的单元越多,故障率越大。
- LVS双机冗余的方式,缺点是所有单元需要双倍的资源投入。
通过Paxos算法来管理所有的naming服务,则可保证high available分配可用的service给client。象ZooKeeper还提供watch功能,即watch的对象发生了改变会自动发notification, 这样所有的client就可以使用一致的,高可用的接口。
3.config配置管理
- 通常手工修改配置文件的方法,这样容易出错,也需要人工干预才能生效,所以节点的状态无法同时达到一致。
- 大规模的应用都会实现自己的配置服务,比如用http web服务来实现配置中心化。它的缺点是更新后所有client无法立即得知,各节点加载的顺序无法保证,造成系统中的配置不是同一状态。
4.membership用户角色/access control list, 比如在权限设置中,用户一旦设置某项权限比如由管理员变成普通身份,这时应在所有的服务器上所有远程CDN立即生效,否则就会导致不能接受的后果。
5.号码分配,如分布式唯一主键生成,分库后自增主键不能唯一表示一条数据,可以利用paxos算法,用master来分配号码,replicate保持同步
4、Zookeeper是否会自动清理日志?
不会,需要运维人员手动清理,写脚本清理
5、Zookeeper是否支持动态扩容?
截止3.4.3版本的zookeeper,还不支持这个功能,在3.5.0版本开始,支持动态加机器了
6、临时节点是一断开就删除么?
答:不是,等到sessionTimeout
7、能为临时节点创建子节点么?
答:不能
8、能否每次都收到watch通知?
答:不能保证
9、为什么将watch设置成一次性通知?
不支持用持久Watcher的原因很简单,永久性的Watcher很费资源,占用带宽,(相当于推模式,推模式的弊端),而是客户端用的时候注册拉取。