- 加载解析配置文件,明确磁盘快照路径、事务日志路径、心跳时间、serverid、集群中的其他服务器地址和端口、角色
- 加载内存DataTree,确定现有数据的最大zxid、epoch等信息
- 加载磁盘快照
- 加载磁盘事务日志
- 根据解析后的配置,拿到了当前serverid,集群规模,其他服务器的地址。每个服务器两两之间开始互相连接。
- 建立连接的第一个包,发送的是服务器的当前状态,基本只有sid,没有最大zxid,因为这里只是建立连接。
- 根据serverid的大小,保证只能大的连小的,这样保证了每两个服务器之间有且只有一个连接,且有向。
- 该连接为长连接,使用BIO的Socket实现,为后面节点的断线感知提供了基础。
- 对于一个服务器而言,它维持了与其他所有服务的长连接,每个连接以及数据传输由一个独立的线程负责,避免单线程的话阻塞在某一个服务器,导致整个通讯阻塞。线程的逻辑很简单,使用队列和其他线程通讯,不断地从队列取数据发到对应的服务器。
- 集群中的服务器连通后,发现当前服务器状态为LOOKING,就会开始投票选主,确定Leader、Follower的角色。注:Observer角色是在配置中指定,在配置解析过程中已经确定完成。
- 一共两种情况会投票选主
- 集群启动的时候,此时无主,要选主。
- 集群运行时,主故障,此时要选主。
- zk是保证CP的,牺牲A可用性,在选主的时候,为了保证一致性,会暂停对外服务,重新创建当前服务器和其他服务器的连接管理器。
- 每个服务器都先投自己,带着自己的serverid,选举周期,最大zxid。将这些封装成票据,放入发送队列,发送给其他远程服务器。
- 如果当前服务器的投票周期过期,重置周期,重置投票箱(map),重新投。
- 远程服务器的投票数据都会添加到队列中,然后从队列中拿出和当前服务器的投票数据进行对比,返回是否应该采用远程的投票(即当前服务器投票的服务器数据较老)
- 远程服务器的epoch更大,返回true
- epoch一样大,远程的zxid更大,返回true
- epoch、zxid一样大,远程的serverid更大,返回true
- 总之一定会比较出来到底用哪个投票的服务器,不会有模棱两可的结果。
- 如果远程服务器的数据较新,当前服务器会修改自己的投票,也投新的服务器,同时会通知其他所以服务器。
- 如果远程服务器的状态是FOLLOWING或者LEADING,就表示已经偷出来谁是leader了,远程的服务器发过来的投票sid就是leader的sid,看看是不是当前服务器,是的话当前就是leader,不是的话,当前就是Follower。
- 选主结束。
- 一共两种情况会投票选主
- 选主结束后,所有learner都会主动连接leader,leader会accept每一个socket(长连接),同时为每一个socket创建并启动一个LearnerHandler线程,交给该线程处理数据。
- 每个learner连接到leader后,发送的第一个包一定是该learner的服务器信息,包括sid,zxid,epoch等。
- leader会比较epoch,根据配置的QuorumVerifier(仲裁算法)来决定用什么epoch作为新的纪元。一般的算法逻辑是,当前leader会比较、计算每一个learner(follower和observer)发来的epoch,然后更新epoch,如果超过一半的具有投票权的Follower数据都处理过了,那么就使用这最后更新的epoch作为新的纪元,后面其他的learner数据都不处理了。
网友评论