数据结构
数据模型的结构与 Unix 文件系统很类似,整体上可以看作是一棵树,每个节点称做一个 ZNode。每一个 ZNode 默认能够存储 1MB 的数据
zookeeper的节点类型
1、顺序节点 create -s 创建znode时设置顺序标识,znode名称 后会附加一个值,顺序号是一个单调递增的计数 器,由父节点维护
2、持久节点 create
3、临时无序节点 create -e
4、临时顺序节点 create -e -s
zookeeper中的集群角色
1、master 领导者
2、flower 跟随者
3、obserber 观察者
observer的优点?
1、当ZooKeeper集群中follower的数量很多时,投票过程会成为一个性能瓶颈,为了解决投票造成的压力,于是出现了observer角色
2、observer无论是读写请求还是投票,都是无状态的,增、减observer的数量不会影响投票结果。这样就可以让一部分server作为follower参与投票,
另一部分作为observer单纯地提供读写服务。这使得ZooKeeper的伸缩性大大提高。
zookeeper时如何处理请求的 zab协议的广播
读请求:
server会直接从它本地的内存数据库中取出数据返回给客户端,这个过程不涉及其它任何操作,也不会联系leader
写请求:
1、收到写请求的那个server,首先将写请求发送给leader
2、leader收到来自follower(或observer)的写请求后,首先计算这次写操作之后的状态,然后将这个写请求转换成带有各种状态的事务(如版本号、zxid等等)
3、leader将这个事务以提议的方式广播出去(即发送proposal)
4、所有follower收到proposal后,对这个提议进行投票,投票完成后返回ack给leader。follower的投票只有两种方式:(1)确认这次提议表示同意;(2)丢弃这次提议表示不同意。
5、leader收集投票结果,只要投票数量达到了大多数的要求(例如,5个节点的集群,3个或3个以上的节点才算大多数),这次提议就通过。
6、提议通过后,leader向所有server发送一个提交通知。
7、所有节点将这次事务写入事务日志,并进行提交。
8、提交后,收到写请求的那个server向客户端返回成功信息。
查看节点的详细数据
cZxid = 0x0 znode 创建节点的事务,每次修改ZooKeeper状态都会产生一个ZooKeeper事务ID。事务ID是ZooKeeper中所 有修改总的次序。每次修改都有唯一的 zxid,如果 zxid1 小于 zxid2,那么 zxid1 在 zxid2 之 前发生。
ctime = Thu Jan 01 08:00:00 CST 1970 znode 被创建的毫秒数
mZxid = 0x0 znode 最后更新的事务 zxid
mtime = Thu Jan 01 08:00:00 CST 1970 znode 最后修改的毫秒数(从 1970 年开始)
pZxid = 0x2 znode 最后更新的子节点 zxid
cversion = 0 znode 子节点变化号,znode 子节点修改次数
dataVersion = 0 znode 数据变化号
aclVersion = 0 znode 访问控制列表的变化号
ephemeralOwner = 0x0 如果是临时节点,这个是 znode 拥有者的 session id。如果不是 临时节点则是 0。
dataLength = 0 znode 的数据长度
numChildren = 2 znode 子节点数量
ZooKeeper的Watch特性
1、Watch是一次性的,每次都需要重新注册,并且客户端在会话异常结束时不会收到任何通知,而快速重连接时仍不影响接收通知。
2、Watch的回调执行都是顺序执行的,并且客户端在没有收到关注数据的变化事件通知之前是不会看到最新的数据,另外需要注意不要在Watch回调逻辑中阻塞整个客户端的Watch回调
3、Watch是轻量级的,WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径。ZooKeeper服务端只会通知客户端发生了什么,并不会告诉具体内容。
Watcher通知状态(KeeperState),KeeperState是客户端与服务端连接状态发生变化时对应的通知类型
枚举属性 说明
Disconnected(0) 客户端与服务器断开连接时
SyncConnected(3) 客户端与服务器正常连接时
AuthFailed(4) 身份认证失败时
ConnectedReadOnly(5) 3.3.0版本后支持只读模式,一般情况下ZK集群中半数以上服务器正常,zk集群才能正常对外提供服务。该属性的意义在于:若客户端设置了允许只读模式,则当zk集群中只有少于半数的服务器正常时,会返回这个状态给客户端,此时客户端只能处理读请求
SaslAuthenticated(6) 服务器采用SASL做校验时
Expired(-112) 会话session失效时
Watcher事件类型(EventType), EventType是数据节点(znode)发生变化时对应的通知类型。
None(-1) 无
NodeCreated(1) Watcher监听的数据节点被创建时
NodeDeleted(2) Watcher监听的数据节点被删除时
NodeDataChanged(3) Watcher监听的数据节点内容发生变更时(无论内容数据是否变化)
NodeChildrenChanged(4) Watcher监听的数据节点的子节点列表发生变更时
zookeeper选举第一次启动
1、服务器1启动,发起一次选举。服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保持为LOOKING;
2、服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服务器2的myid比自己目前投票推举的(服务器1)大,
更改选票为推举服务器2。此时服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING
3、服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3。此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服
务器3的票数已经超过半数,服务器3当选Leader。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING
4、服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为
1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLLOWING;
zookeeper选举非第一次启动(集群中不存在leader)
SID: 服务器ID。用来唯一标识一台 ZooKeeper集群中的机器,每台机器不能重复,和myid一致。
ZXID: 事务ID。ZXID是一个事务ID,用来标识一次服务器状态的变更。在某一时刻集群中的每台机器的ZXID值不一定完全一致
Epoch: 每个Leader任期的代号。没有Leader时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加
1、假设ZooKeeper由5台服务器组成,SID分别为1、2、3、4、5,ZXID分别为8、8、8、7、7,并且此时SID为3的服务器是Leader。
某一时刻,3和5服务器出现故障,因此开始进行Leader选举
(EPOCH,ZXID,SID ) (EPOCH,ZXID,SID ) (EPOCH,ZXID,SID )
SID为1、2、4的机器投票情况: (1,8,1) (1,8,2) (1,7,4)
选举Leader规则: EPOCH大的直接胜出 2EPOCH相同,事务id大的胜出 3事务id相同,服务器id大的胜出
拜占庭将军问题
拜占庭将军问题是一个协议问题,拜占庭帝国军队的将军们必须全体一致的决定是否攻击某一支敌军。问题是这些将军在地理上是分隔开来的,并且将 军中存在叛徒。叛徒可以任意行动以达到以下目标:
zab协议的崩溃恢复流程:
什么情况下会进入崩溃恢复流程?
1、当服务器启动时
2、当leader 服务器出现网络中断,崩溃或者重启的情况
3、当集群中已经不存在过半的服务器与Leader服务器保持正常通信。
协议崩溃恢复要求满足以下两个要求:
1、已经被处理的事务请求(proposal)不能丢(commit的
2、没被处理的事务请求(proposal)不能再次出现
什么时候会出现事务请求丢失呢?
1、leader收到半数以上proposal的ack之后,commit到本地并且向客户端返回成功,但是在各个 follower 在收到 COMMIT 命令前 leader 就挂了,导致剩下的服务器并没有执行都这条消息
1、选举拥有 proposal 最大值(即 zxid 最大) 的节点作为新的 leader: 由于所有提案被 COMMIT 之前必须有合法数量的 follower ACK,即必须有合法数量的服务器的事务日志上有该提案的 proposal, 因此,zxid最大也就是数据最新的节点保存了所有被 COMMIT 消息的 proposal 状态。
2、新的leader将自己事务日志中proposal但未COMMIT的消息处理。 3、完成Leader选举后,在正式开始工作前,Leader服务器首先会确认日志中的所有Proposal是否都已经被集群中的过半机器提交了, 即是否完成了数据同步。Leader服务器需要确所有的Follower服务器都能够接收到每一条事务Proposal,并且能够正确地将所有已经提交了的事务Proposal应用到内存数据库中。 Leader服务器会为每个Follower服务器维护一个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器, 并在每一个Proposal消息后面紧接着再发送一个Commit消息,以表示该事务已经被提交,等到Follower服务器将所有其尚未同步的事务Proposal都从Leader服务器上同步过来并成功应用到本地数据库后, Leader服务器就会将该Follower服务器加入到真正的可用Follower列表并开始之后的其他流程。 2、当leader 接收到消息请求生成 proposal 后就挂了,其他 follower 并没有收到此 proposal,因此经过恢复模式重新选了 leader 后,这条消息是被跳过的。 Zab 通过巧妙的设计 zxid 来实现这一目的。一个 zxid 是64位,高 32 是纪元(epoch)编号,每经过一次 leader 选举产生一个新的 leader,新 leader 会将 epoch 号 +1。 低 32 位是消息计数器,每接收到一条消息这个值 +1,新 leader 选举后这个值重置为 0。 这样设计的好处是旧的 leader 挂了后重启,它不会被选举为 leader,因为此时它的 zxid 肯定小于当前的新 leader。 当旧的 leader 作为 follower 接入新的 leader 后,新的 leader 会让它将所有的拥有旧的 epoch 号的未被 COMMIT 的 proposal 清除。
zookeeper的最终一致性
强一致性:数据a一旦写入成功,在任意副本任意时刻都能读到a的最新值。也称为原子一致性
弱一致性:写入一个数据a成功后,在数据副本上可能读出来,也可能读不出来。不能保证多长时间之后每个副本的数据一定是一致的,其实就是不需要一致。
最终一致性:写入一个数据a成功后,在其他副本有可能读不到a的最新值,但在某个时间窗口之后保证最终能读到。 可以看做弱一致性的一个特例,这里面的重点是这个时间窗口。
在 zookeeper 集群内部的数据副本同步,是基于过半提交的策略,意味着它是最终一致性。
zookeeper的顺序一致性模型
1、顺序一致性是按照客户端的指令顺序,所有的进程以相同的顺序看到所有的修改。读操作未必能及时得到此前其他进程对同一数据的写更新(因为有二阶段commit的操作)。但是每个进程读到的该数据的不同值的顺序是一致的,如果需要读取到相同的值需要自信api中的sync方法
2、zookeeper 通过Zxid全局事务id来保证数据一致性
zookeeper与cap
1、zookeeper保证的是CP,因为Leader选举的时候整个服务都是不可用的
zookeeper的单一视图保证
当客户端正常连接集群中的一台服务器时,每次请求响应都会返回所连接的服务器上所处理的最新的事务id并进行记录。
当客户端由于各种原因导致与服务器之间断连时,客户端将会以自己看到代表数据版本信息的zxid一起发送给服务端,请求建立连接。
服务端在收到连接建立请求时,会进行判断:自己的数据版本没有客户端之前看到的数据版本新,服务端将会拒绝连接建立。
从而保证客户端不会连接到数据版本比自己之前看到的旧的服务器上,因此不会请求到老版本的数据,从而保证了单一视图特性。
选主流程简单总结
选举机制
半数机制,超过半数的投票通过,即通过。
(1)第一次启动选举规则:
投票过半数时,服务器 id 大的胜出
(2)第二次启动选举规则:
1、EPOCH 大的直接胜出
2、EPOCH 相同,事务 id 大的胜出
3、事务 id 相同,服务器 id 大的胜出
zookeeper 分布式锁等应用场景
1、分布式API目录。为分布式系统中各种API接口服务的名称、链接地址提供类似JNDI(Java命名和目录接口)
2、分布式ID生成器,使用持久顺序节点
3、分布式锁
1、一个ZooKeeper分布式锁需要创建一个父节点,尽量是持久节点(PERSISTENT类型),然后每个要获得锁的线程都在这个节点下创建一个临时顺序节点,因为ZooKeeper节点是按照创建的次序依次递增的。
2、编号最小的那个节点表示获得了锁。所以,每个线程在尝试占用锁之前首先判断自己的排号是不是当前最小,如果是就获取锁。
3、释放锁的时候需要删除创建的ZNode。创建成功后,如果不是排号最小的节点,就处于等待通知的状态。前一个ZNode删除的时候,会触发ZNode事件,当前节点监听到删除事件就是轮到了自己占有锁的时候。第一个通知第二个,第二个通知第三个,击鼓传花似的依次向后。
4、ZooKeeper的内部优越机制能保证由于网络异常或者其他原因,集群中占用锁的客户端失联时锁能够被有效释放。一旦占用ZNode锁的客户端与ZooKeeper集群服务器失去联系,这个临时ZNode也将自动删除。排在它后面的那个节点也能收到删除事件,从而获得锁。
4、分布式协调工具
1、,A系统发送个请求到 mq,然后 B 系统消息消费之后处理了。那 A 系统如何知道 B 系统的处理结果?用 zookeeper 就可以实现分布式系统之间的协调工作。
A 系统发送请求之后可以在 zookeeper 上对某个节点的值注册个监听器,一旦 B 系统处理完了就修改 zookeeper 那个节点的值,A 系统立马就可以收到通知,完美解决。
5、配置中心,也就是发布者将数据发布到ZooKeeper的一个节点或者一系列节点上,提供订阅者进行数据订阅,从而实现动态更新数据的目的,实现配置信息的集中式管理和数据的动态更新
6、所有服务会尝试创建Master临时节点,谁创建成功谁就是Master,其他的两台就作为Slave。所有的Work Server必需关注Master节点的删除事件。通过监听Master节点的删除事件,
来了解Master服务器是否宕机(创建临时节点的服务器一旦宕机,它所创建的临时节点即会自动删除)。一旦Master服务器宕机,必需开始新一轮的Master选举。
7