Zookeeper和ZAB协议
Zookeeper简介
Zookeeper是一个典型的分布式数据一致性解决方案,其设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。
核心概念
Zookeeper数据存储模型
Zookeeper数据模型是由一系列被称为ZNode的节点组成节点树,其中根节点为/。ZNode节点包含了数据date、状态stat、访问权限ACL和子节点引用children。
节点有四种类型:
- 持久节点(persistent):节点创建后一直存在,直到被主动删除。
- 持久有序节点(persistent_sequential):子节点有序的持久节点。
- 临时节点(ephemeral):临时节点的生命周期和客户端会话绑定,客户端与Zookeeper断开连接后节点就会消失。临时节点只能做叶子节点,不能创建子节点。
- 临时有序节点(ephemeral_sequential):子节点有序的临时节点。
ZNode存储自身状态的数据结构叫做stat,如下(Zookeeper版本3.6.0):
| 状态属性 | 说明 |
|---|---|
| cZxid | create zxid,创建节点的zxid |
| ctime | create time,节点创建时间 |
| mZxid | modified zxid,最后一次更新的zxid |
| mtime | modified time,最后一次更新时间 |
| pZxid | 最后一次更新子节点的zxid,只有子节点列表变更才会更新pZxid,子节点内容变更不会更新 |
| cversion | 子节点版本号 |
| dataVersion | 数据版本号,创建时为0,每次更新加1 |
| aclVersion | ACL版本,修改ACL加1 |
| ephemeralOwner | 临时节点的sessionId;持久节点此值为0 |
| dataLength | 数据的长度 |
| numChildren | 子节点个数 |
Zookeeper会为每一个事务生成一个唯一且递增长度为64位的zxid,zxid由两部分组成:低32位表示计数器(counter)、高32位标识纪元号(epoch)。epoch在当前leader成为leader时生成。
事件监听
Zookeeper常用API有create、delete、exists、getData、getChildren等,其中exists、getData、getChildren属于读操作。客户端在请求读操作的时候,可以选择是否设置Watcher,即注册特定时间的触发器。
在ZNode发生改变时,将会触发对应的事件,Zookeeper会将事件异步通知给这些客户端。
Zookeeper集群
Zookeeper集群角色
Zookeeper集群的Server分为以下三种角色:
- Leader:Leader作为整个Zookeeper集群的主节点,负责响应所有对Zookeeper状态变更的请求。它会将每个写请求进行排序和编号,以保证整个集群内部消息处理的FIFO。
- Follower:Follower除了响应本服务器上的读请求外,还要处理Leader的提议,并在Leader提交该提议时在本地也进行提交。
- Observer:为客户端提供读服务,如果是写请求则转发给Leader,不参与Leader选举,不参与“过半写成功”。
Zookeeper集群中节点状态
- Looking:当前Server不知道Leader是谁,正在搜寻。
- Leading:当前Server即为选举出来的Leader。
- Following:Leader已经选举出来,当前Server与之同步。
- Observing:当前节点为Observer。
Zookeeper集群搭建注意事项
-
Zookeeper集群搭建需要奇数个节点,最少3个节点。
-
需要在数据目录data下创建一个myid文件,记录该节点在集群中的id。
-
在zoo.cfg中添加集群配置:
# zk集群配置 server.1=172.16.0.2:2888:3888 server.2=172.16.0.3:2888:3888 server.3=172.16.0.4:2888:3888Zookeeper集群配置格式为server.A=B:C:D[:observer],A是节点myid,B是ip,C是Leader与其它节点交换信息的端口,D是选举时使用的端口。
Zab协议
Zab协议(Zookeeper Atomic Broadcast Protocol)是为Zookeeper设计的一种支持崩溃恢复的一致性协议。
Zab协议中有两种模式:崩溃恢复和消息广播。
崩溃恢复
当服务器启动时、Leader崩溃或Leader失去大多数的Follower时集群会进行崩溃恢复。
崩溃恢复分为三个阶段:
-
选举
默认的选举算法是FastLeaderElection。
此时集群节点都处于Looking状态。每个节点向其它节点发出一个投票,包含自身的myid和最近一次事务的zxid。
接下来节点会比较自身和收到的的zxid,如果发现收到的zxid更大,就变更投票给目前已知最大zxid所属的节点,同理,zxid相同时选择myid大的。
每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准Leader,状态变为Leading。其它节点的状态变为Following。
-
发现
用于在Follower节点中发现最新的zxid和事务日志。
-
同步
把Leader收集的最新事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才会成为正式的Leader。
之后,集群进入消息广播模式。
消息广播
Zookeeper常规情况下更新数据的时候,由Leader广播到所有的Follower。过程如下:
- 客户端写请求给任意Follower;
- Follower把写请求转发给Leader;
- Leader将写请求转换为事务Proposal,并将该Proposal分发给集群中所有的Follower;
- Follower接到Propose消息,写入日志成功后,返回ACK给Leader;
- Leader接到半数以上ACK消息,返回成功给客户端,并且广播Commit请求给Follower。
Leader和每个Follower之间通过消息队列异步。
和大多数数据库系统一样,Zookeeper对于每个写操作会先WAL(Write-Ahead-Logging),再对内存中的数据做更新,然后向Client响应更新结果。定期将内存数据落盘。
常见问题
-
为什么Zookeeper集群要部署奇数台服务器?
Zookeeper集群宕掉几个节点后,剩下的个数必须超过原来的半数集群才可以继续运行。无论奇偶都可以选举leader。5个节点最多宕掉2个,6个也是最多宕掉2个,2n个和2n-1个的容忍度是一样的。从节省资源的角度看,没必要部署偶数个。
-
脑裂问题?
脑裂:集群由于网络分区形成了多个小集群,每个小集群都选举出各自的Master或Leader。
Zookeeper集群有过半数可用的要求,少于半数的小集群无法选举,因此不会有脑裂问题。
-
Zookeeper羊群效应?
当被监控的节点发生变化时,Zookeeper会通知所有监控的客户端。如果监控的客户端数量过多,就会造成Zookeeper性能突然下降。
因此要避免在一个特定节点设置大量的监控。