背景
继上一篇的zookeeper的学习,上一篇主要偏向于zookeeper的总体结构的熟悉和使用层面。 本文主要是记录自己学习其内部的选举算法,一致性处理策略。
过程
在开始之前,推荐几篇比较不错的文章:
(选举算法)
(代码解析)
zookeeper server
入口:org.apache.zookeeper.server.quorum.QuorumPeerMain
通过QuorumPeerConfig读取zoo.cfg配置文件,myid信息等。 具体的参数可以参考官方文档:
主要关注几个参数:peerType=observer/participant (是否是观察者,或者参与Leader/Follower)
electionAlg=0/1/2/3/ (选举算法)
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
group.1=1:2:3 (机器分组)
group.2=4:5:6
group.3=7:8:9
weight.1=1 (某机器权重)
weight.2=1
weight.3=1<span style="white-space: normal;"> </span>
创建QuorumPeer,启动服务。
首先加载本地的zkDatabase
startLeaderElection,发起一次Leader选举。 选举算法:LeaderElection / FastLeaderElection / AuthFastLeaderElection
确定自己的角色后,进行响应的角色初始化:
OBSERVING : observer.observeLeader();FOLLOWING : follower.followLeader();LEADING : leader.lead();
LeaderElection算法:
每个机器节点加入,都发起一次Vote。
zookeeper server节点接受到新一轮的Vote,都返回上一轮的Leader选举的最后Vote结果。如果处于LOOKING,比如第一次加入的机器,则Vote自己
发起Vote的机器,收集完所有server的Vote结果(包括自己),统计一下结果,并且判断一下是否超过半数
if (result.winningCount > (self.getVotingView().size() / 2))
如果没有超过半数,则记录一下本次的投票结果。下一次Vote时,就直接投给上一轮票数最高的Vote,继续进入步骤3
每个机器节点加入,都向外部建议自己做为Leader。
zookeeper server接受到请求后,进行一个抉择:
if ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))
单个机器上收到了其他所有机器的Vote结果后,判断出最后的winner结果
启动LearnerCnxAcceptor,监听Follower的请求。针对每个链接的Follower,开启一个新线程LearnerHandler进行处理
然后针对所有的LearnerHandler,while(true)进行心跳检查ping。针对每次ping结果,针对有ack响应的结果进行vote统计,如果发现超过半数失败,则退出当前的Leader模式,这样又是发起新一轮的选举
建立socket,链接到Leader server上
发起一次syncWithLeader同步请求,此时Leader会根据当前的最大的zxid对比客户端的zxid,发送DIFF/TRUNC/SNAP指令给follower上,保证follower和leader之间的数据一致性。
同步完成后,就进入while(true){read , process}的工作模式,开始接受Leader的指令并处理
PING : 响应AckPROPOSAL : 记录到自己的缓冲队列pendingTxns中,等待sync指令后刷新到zkDataBase中。有点类似2PC阶段事务COMMIT : 刷新pendingTxns中的对应记录SYNC : 客户端的同步request请求
建立socket,链接到Leader server上
发起一次syncWithLeader同步请求
同步完成后,进入while(true){read , process}的工作模式,和Follower有点不同,主要在于其不处理PROPOSAL/COMMIT等2PC阶段。只接受INFORM,Leader告诉最终的更新结果,Observer直接提交到zkDataBase中。
PrepRequestProcessor -> ProposalRequestProcessor -> CommitProcessor -> toBeAppliedProcessor -> FinalRequestProcessor处理顺序
PrepRequestProcessor主要处理ACL控制,不细讲,自己看看代码就懂的
ProposalRequestProcessor,处理有点特殊。因为Leader server同样会提供客户端API的create操作,所以需要做特殊处理
1. 如果请求是LearnerSyncRequest,表明是leader做为server提供给客户端服务,并且接受到客户端的sync请求。2. 否则认为是需要进行决策,发给所有的follower进行投票。(这里的请求可能来自于follower的写动作请求leader决策,也可能是leader做为server自己接收到的请求),通过AckRequestProcessor先给自己投票。注意: ProposalRequestProcessor只会针对create/delete等写操作会发起一次Vote。 读操作会直接交由后续的Processor进行处理CommitProcessor主要是一些等待commit处理的request,接收到sync指令后就会提交到下一个Processor进行处理。
做为leader,会在收集足够的Vote后,自己会给自己发送一次sync指令做为Follower,会受到Leader发送的Sync指令,然后自己提交commit动作toBeAppliedProcessor: 主要的作用就是记录当前正在提交到zkDatabase中的request数据,保证新的Follower连接上来时,能获取到这些处于内存中"正准备提交"的数据
FinalRequestProcessor : 处理最后的请求,比如读/写请求,此时所有的server都会同步的写入数据。
接受客户端请求: FollowerRequestProcessor -> CommitProcessor -> FinalRequestProcessor
1. 客户端接受请求后,针对一些写动作(比如create,delete,setData,setAcl等),FollowerRequestProcessor会发起一个request,请求leader进行所有server的一致性的控制。 同时会将request请求递交到commitProcessor2. CommitProcessor请求,会等待leader的sync request请求,会提取出相对应的request,提交到下一个processor进行处理。3. FinalRequestProcessor,处理最终的指令请求。因为一些写动作通过follower->leader的交互已经得到Leader的ack,会保证每个节点都能写入request数据。处理Leader的PROPOSAL/COMMIT: SyncRequestProcessor -> SendAckRequestProcessor。 (主要是接受Leader广播的其他server的数据变更事件,保证所有server都能得到同步更新)
1. 主要是接受到Leader的PROPOSAL request后,先将request暂存于pendingTxns,并提交到SyncRequestProcessor的queue队列,排队进行后续发送Ack给Leader处理2. 接受到Leader端的sync/commit指令,将pending的request请求提交到当前server的zkDataBase中。
接受断乎端请求: ObserverRequestProcessor -> CommitProcessor -> FinalRequestProcessor
![点击查看原始大小图片 d842653f-ec06-3a7d-8f6b-897406571661.png](http://dl.iteye.com/upload/p_w_upload/508322/d842653f-ec06-3a7d-8f6b-897406571661.png)
![点击查看原始大小图片 c1e54617-97ea-3735-859d-0768e12ac6de.png](http://dl.iteye.com/upload/p_w_upload/508320/c1e54617-97ea-3735-859d-0768e12ac6de.png)
![点击查看原始大小图片 1985df2e-68bf-3a9c-8b3d-4562987d05af.png](http://dl.iteye.com/upload/p_w_upload/508318/1985df2e-68bf-3a9c-8b3d-4562987d05af.png)
最后
感慨几下:
1. 彪悍的图,画的比较精细,不是真正了解Follower/Leader之间的数据处理,不太容易看的懂
2. zookeeper开发者仅仅通过Tree树状结构,可以实现分布式一致性控制,可以衍生出Barrier,Lock等功能。感叹人的智慧
3. 感叹淘宝的同学,发觉在rdc.taobao.com/上有n多人研究分布式的相关内容,而且文章质量都不错。为啥同样是阿里的子公司,学习的氛围咋就差这么多呢,可能一个原因还是数据量的问题。