ZK简介
1 会话
会话:客户端与服务端创建并保持TCP连接的过程。
1.1 会话状态
当客户端与服务端成功完成连接创建后,就会建立一个会话。ZK会话在整个运行期间的生命周期,会在不同的会话状态之间进行切换:
- CONNECTING
- 一旦客户端开始创建对象,客户端状态就会为CONNECTING
- 网络闪断或其他原因,导致客户端和服务器端之间连接断开
- CONNECTED
- RECONNECTING
- RECONNECTED
- CLOSED
- 会话超时
- 权限校验失败
- 客户端主动退出程序
1.2 会话基本信息
Zookeeper使用SessionImpl
实现来表示会话的基本信息
public static class SessionImpl implements Session {
// sessionId全局唯一
final long sessionId;
// 会话的超时时间
final int timeout;
// 下次会话的超时时间
long tickTime;
// 会话是否关闭
boolean isClosing;
}
1.3 会话管理
ZK的会话管理称为分桶策略
:将类似的会话放在同一块区域中进行管理,便于ZK对会话进行不同区块的隔离处理,以及同一区块的统一处理。如下图:
会话激活
为保证会话的有效性,ZK在运行过程中,客户端会在会话超时时间过期范围内向服务端发送PING请求保持会话的有效性。同时服务端不断地接收来自客户端的心跳检测,并且需要重新激活对应的客户端会话,称之为TouchSession
激活会话的情况有2种:
- 客户端向服务端发送请求:读或写请求
- 客户端发现sessionTimeout/3时间内,未与服务端进行通信,客户端主动发起PING请求
1.4 会话清理
会话清理步骤:
- 标记会话为"已关闭"
isClosing标记为true,即使会话清理期间接收到该客户的新请求,也不会处理
- 发起会话关闭
- 收集需要清理的临时节点
临界情况考虑,在会话关闭请求之前,正好有如下2类请求达到,并正在处理中:
假如当前收集到的临时节点列表为tmpNodes。下面两种情况的共同点是,事务处理尚未完成,因此还没有应用到内存数据库中。- 节点删除请求:将节点从tmpNodes中移除,避免重复删除。
- 临时节点创建请求:将请求对应的数据节点路径添加到tmpNodes节点集中
- 添加"节点删除"事务变更
将临时节点集逐个转换成"节点删除"请求,并放入事务变更队列中
- 删除临时节点
删除内存数据库中tmpNodes对应的临时节点
- 移除会话
- 关闭NIOServerCnxn
1.5 重连
客户端和服务端在网络断开期间,ZK客户端会自动进行重连,直至成功连接到ZK集群中的某个服务器。这种情况下,客户端可能会处于如下两种状态:
- CONNECTED:若在超时时间内连接上,重连成功
- EXPIRED:若超时时间外连上,服务端其实已经认定该会话失效,进行了清理操作。因此为非法会话
ZK中客户端和服务端之间维持TCP长连接,在连接期间,若客户端和服务端出现连接断开后,客户端可能会看到如下几种异常:
- CONNECTION_LOSS,连接断开
CONNECTION_LOSS情况下,客户端会自动从地址列表中重新逐个选取新的地址并尝试重新连接。
- SESSIN_EXPIRED,会话失效
重连时间超过了会话超时时间,服务端认为该会话结束,会清理该会话。但客户端本身不知道会话已经失效,且状态仍未DISCONNECTED,之后客户端重新连接服务器,此时服务器会告诉客户端会话失效。
- SESSION_MOVE,会话转移
客户端会话从A服务器转移到B服务器上(断开重连到B服务器)。在3.2.0版本之前,会话转移没有被明确提出,因此会出现如下异常情况
- ZK集群有S1、S2、S3。C1和S1建立会话:
T1时刻,C1向S1发送R1请求:SetData("/ses/test", 1),但请求发送刚结束,连接断开。
T2时刻,C1重新连接到S2,并发送R2请求:SetData("/ses/test", 2)。
结果R2请求先处理,R1请求之后处理。
因此之后,ZK提出会话转移的概念。服务端在处理客户端请求时,检查会话的owner,若会话owner不是当前服务器,会抛出SessionMovedException异常。
- ZK集群有S1、S2、S3。C1和S1建立会话:
2 分布式一致性
分布式系统中,为了保持事务处理的ACID特性,引入了协调者
来统一调度所有参与者
的事务请求。基于该思想,衍生出了2PC和3PC协议。
2.1 二阶段提交
2PC是一种一致性协议,用于保证分布式系统数据的一致性。目前绝大多数关系型数据库都采用2PC协议来完成分布式事务(XA Transaction)。协议的执行流程如下:
2.1.1 阶段1:提交事务请求
- 发送事务请求
协调者
向所有参与者
发送事务内容,询问是否可以执行事务提交操作,并开始等待所有参与者
的响应- 执行事务
所有参与者
执行事务,将Undo和Redo信息记入事务日志- 反馈事务请求结果
若参与者
成功执行了事务,反馈给协调者
成功响应;否则反馈失败响应
2.1.2 阶段2:执行事务提交
该阶段有2种情况:执行事务提交、中断事务。
1. 事务提交
若所有参与者
向协调者
反馈的都是成功响应
协调者
向所有参与者
发送提交事务请求参与者
执行事务提交,提交完成释放事务资源参与者
将事务提交后,反馈提交结果给协调者
协调者
完成事务
2. 中断事务
若任一参与者
向协调者
反馈失败响应、或协调者
等待超时等等。
协调者
向所有参与者
发送回滚事务请求参与者
根据Undo日志进行事务回滚,提交完成释放事务资源参与者
将事务回滚后,反馈提交结果给协调者
协调者
完成事务中断
2.2 ZAB协议
2.2.1 Zookeeper分布式一致性特性
- 顺序一致性。同一个Client发起的事务请求,最终将严格地按照其发起顺序被应用到Zookeeper中。
- 原子性。所有事务请求的处理结果在整个分布式系统中的所有节点数据一致。
- 单一视图。无论Client连接的是哪个Zookeeper服务器,其看到的服务端数据模型都是一致的
- 可靠性。事务所引起的服务端状态变更,会被一直保留,除非又有新事务对其变更
- 实时性。Zookeeper仅保证在一定时间范围内,Client最终一定能从服务端读取到最新的数据状态。
2.2.2 ZAB协议概述
ZAB:Zookeeper Atomic Broadcast,原子消息广播协议(一致性协议)。它是一种特别为ZK设计的崩溃可恢复的原子消息广播算法。ZAB协议有2种模式:崩溃恢复和消息广播。
协议简介
- 当整个应用启动时,或Leader崩溃退出、重启、网络中断等异常情况下,ZAB协议会进入恢复模式并选举产生新的Leader服务器。
启动时、或Leader崩溃,服务器会进入到LOOKING状态,开启LEADER选举过程
- 当选举出新的Leader,同时集群中已经有过半的机器与该Leader完成了状态同步后,ZAB协议就会退出恢复模式,进入消息广播模式。
消息广播
类似二阶段提交过程。Leader会为每个Follower都各自分配一个单独的队列,针对需要广播的事务请求(Proposal)依次放入到这些队列中,并且根据FIFO策略进行消息发送。
Follower接收到Proposal后,首先将Proposal以事务日志的形式写入到本地磁盘,并在写入成功后反馈给Leader,Leader接收到超过半数的响应后,就会广播COMMIT消息让所有的Follower提交事务,同时Leader自身也会提交事务。
崩溃恢复
一旦Leader服务器崩溃或Leader服务失去了过半的Follower联系,就会进入崩溃恢复模式,此时需要进行Leader的选举(事务编号ZXID):
- 变更Follower的状态
当leader挂了,Follower会将自己的服务器状态变更为LOOKING
- 发起投票
每个服务器都发起一个投票(myid, ZXID)。
- 仲裁投票
每个服务器都会选举:首先对比ZXID最大,选择ZXID最大的投票,当ZXID相等时就选择myid最大的服务器
- 统计投票
某个投票超过半数则认为是Leader,其余服务器为Follower