记录一下一些关于zk的思考和总结
异常场景
- Zk客户端连接节点1,watch了某个节点,节点1挂掉之后,zk客户端重新连接别的节点(比如节点2),watch事件还会生效吗
- Zk客户端会在本地保存watch事件
- Zk客户端重新连接节点之后,都会根据本地保存的watch事件重新发送一次watch请求给新连接的节点
- Zk客户端连接节点1,创建了临时znode,如果节点1挂掉了,创建的临时znode会被删除吗
- Zk客户端给节点1发送请求或者心跳,发现节点1挂掉了,会进行重连
- 重连时,会使用之前的session id进行认证,认证成功就能继续保持之前的会话,创建的临时znode不会被删除
- Zk连接节点之后,需要进行认证,认证时,如果没有带session id,就是重新开启一个会话,如果有带session id,认证成功会保持之前的会话
- Zk客户端连接节点1(主节点是节点3),创建了临时znode,如果节点1跟原来的zk集群发生了网络分区,创建的临时znode会被删除吗
- Zk客户端连接节点1的会话信息,节点1发送给主节点,主节点会维护全局的会话信息
- 节点1跟原来的zk集群发生了网络分区,可能会出现两种情况(是我猜的,暂时不知道真实是发生哪一种情况)
- Zk客户端给节点1发送心跳,节点1回复ok,但是节点1没有办法把会话信息发送给主节点,主节点的会话信息没有更新,到了过期时间,主节点就会把zk客户端创建的临时节点删除
- Zk客户端给节点1发送心跳,节点1发现没有办法与主节点进行通信了(重新发起选举,但是无法成功选举,因为分区节点数量不足),返回给客户端连接错误,客户端与其他节点进行重连(使用之前的session id),重连成功后,主节点的会话信息更新
- Zk客户端连接zk集群,进行leader选举,zk客户端与zk集群的网络发生分区,zk客户端无法连接zk集群,该客户端所创建的临时节点会被主节点删除,其他客户端会成为leader,那么该客户端该怎么感知自己已经不是leader
- Zk客户端连接zk集群时,设置事件回调函数,针对SessionEvent事件进行处理,发现断开连接之后,就降级为follower
- 如果只是zk客户端连接的节点挂掉了,zk客户端会收到StateDisconnected事件,先降级为follower
- 之后zk客户端会进行重连,重连成功之后,zk客户端会收到StateConnected事件,之后zk客户端可以检查之前创建的临时节点是否还在,并且检查是否是最小的节点,如果是,则重新提升为leader
- Zk客户端连接zk集群时,设置事件回调函数,针对SessionEvent事件进行处理,发现断开连接之后,就降级为follower
一致性
- 写的顺序一致性
- 同一个客户端的写请求,肯定是按照提交的顺序进行提交的
- leader提交议案是线性的,提交议案是按照提出议案的顺序进行的,提交一个议案,其议案前面的议案必定已提交
- 读的顺序一致性
- zookeeper不能保证所有客户端都能马上读取到最新版本的数据,但是能保证客户端(同一个会话下)在读取到新版本的数据之后,必定不会读取到旧版本的数据
- 客户端使用旧的会话进行重连时,zookeeper服务端会校验客户端的会话的zxid与服务端的zxid,如果客户端的zixd比较新,则会拒绝连接,因为客户端的zxid比较新,证明客户端之前连接的zk服务端的同步进度比较新,为了保证同一个会话中不会出现读取到新版本后又读取到旧版本的数据的情况的,不允许客户端与zxid比其旧的服务端进行连接。
- zookeeper不能保证所有客户端都能马上读取到最新版本的数据,但是能保证客户端(同一个会话下)在读取到新版本的数据之后,必定不会读取到旧版本的数据
Zab协议
- 消息广播
- leader节点发送议案给follower,过半数的follower回复ack之后,leader节点就提交该议案
- 可以理解为没有中断(rollback)逻辑的二段提交,follower的数据必须跟leader节点一致,所有的follower要么正常反馈leader提出的议案,要么就抛弃leader
- leader崩溃退出造成的数据不一致交给崩溃恢复协议去解决
- 过半数的follower回复ack之后,leader节点就提交该议案,能够保证提交的数据不丢失
- 提交之后,leader节点崩溃,需要重新选举leader,从follower中选举leader时,是看follower执行过的历史议案的最大的zxid,拥有最大的zxid会被选举成leader,能选举出来的leader必定是包含以提交的数据的
- 假设集群是5个节点,节点1是leader节点,刚刚提交了议案p3,节点2和3已经写入了议案p3,节点4和5还没有写入,这个时候节点1和节点2崩溃,节点345进行选举,节点3会当选,因为节点3的zxid最大,如果节点3也崩溃了,leader就选不出来了
- 提交之后,leader节点崩溃,需要重新选举leader,从follower中选举leader时,是看follower执行过的历史议案的最大的zxid,拥有最大的zxid会被选举成leader,能选举出来的leader必定是包含以提交的数据的
- leader节点发送议案给follower,过半数的follower回复ack之后,leader节点就提交该议案
- 崩溃恢复
- leader崩溃之后,这个时候需要进行选举了,这个时候就需要各个follower节点都达成一个共识:投票给zxid最大的follower节点,让其当选leader
- zab协议考虑以下两个原则来保证数据的一致性
- 需要确保那些已经在leader节点上提交的议案最终被所有的服务器提交
- 需要确保丢弃那些只在leader节点上被提出的事务
- 不能确保丢弃那些已经在部分但是不足过半数的follower上写入的议案,因为如果这些follower没有崩溃,这些follower中的其中一个必定会被选举为leader,那么该议案最终会被leader提交
- 假设集群是5个节点,节点1是leader节点,leader节点写入议案p4,然后leader给所有follower节点发送写入议案p4的请求
- leader还没来得及给所有follower节点发送请求就崩溃了,也就是没有follower节点接收过议案p4,之后选举出来的leader节点就不会包含议案p4
- 旧leader恢复之后,跟新leader节点进行同步,发现p4在leader节点中不存在,需要抛弃
- 需要借助leader周期来判断p4是否在新leader节点中存在,因为如果没有leader周期的话,识别不出p4到底是新leader选举出来之后接收的新议案,还是新leader选举之前就存在的旧议案
- Leader给节点2发送完写入议案p4的请求之后就崩溃了,节点2写入了议案p4
- leader节点崩溃,节点2当选leader,议案p4不会丢失(旧leader节点恢复后p4不会被丢弃)
- leader节点和节点2崩溃,议案p4会丢失(旧leader节点和节点2恢复后p4会被丢弃)
- 不能确保丢弃那些已经在部分但是不足过半数的follower上写入的议案,因为如果这些follower没有崩溃,这些follower中的其中一个必定会被选举为leader,那么该议案最终会被leader提交
- zab协议选择集群中拥有最大zxid的节点作为leader,就能确保提交已经提交的议案,同时丢弃只在leader节点上被提出的事务
- 同步
- 简单理解
- 消息广播阶段:由 leader 写入提交议案,follower 只能接受(不能接受的就退出集群),保证了写入的一致性;过半数的follower写入了议案才提交议案,保证数据不会丢失。
- 崩溃灰度阶段:leader节点崩溃了,需要重新选举一个leader,而选举leader需要所有follower达成一个共识,而zab协议的选举共识就是:zxid最大的follower节点当选(zxid一样大就看机器id)