4. zk启动过程
OpCode.setDataOpCode.deleteOpCode.multiOpCode.createOpCode.setACLOpCode.closeSessionMinCommited ZxidMaxCommited ZxidprocessTxncommittedLogProposalProposalProposalProposalProposalDataTree1、取100个快照文件按时间倒叙2、寻找第一个数据完整并进行序列化成功的快照文件3、然后将日志文件中所有大于lastZid的日志应用到DataTree和sessionsWithTimeout中,并更新LastXzidrestoreAdd 最多保留500个FileSnapPlayBackListenerPurgeTask(TimerTask定时清理日志)通过可以修改日志level等入口FileTxnSnapLogrestoreLog4jMBeans FileTxnLogdoWorkRegiesterZKDatabasesessionsWithTimeoutsDatadirCleanupManagerQuorumPeerMainloadDataBase①sessionId—timeoutsessionId—timeoutsessionId—timeoutsessionId—timeoutsessionId—timeoutsessionId—timeoutsessionId—timeoutsessionId--timeoutQuorumPeerStartConfigFilesetConfig②QuorumPeerConfig③NIOServerCnxnFactoryThreadNIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181doWorkNIOServerCnxndoIOInput Args④tickTime,quorumVerifier等startLeaderElectiondoAcceptSocketChannelAcceptServerSocketChannelCreate/startZkServerThreadQuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181doWorkCreatecreateThreadwhf1/192.168.137.10:3888sendNotificationslookForLeaderFastLeaderElectioncurrentVote(myid,lastZxid,currentEpoch)QuorumCnxManager.ListenerdoWorkServerSocket(port:3888)ThreadWorkerSender[myid=1]WorkerSenderQuorumCnxManagerMessengerstartdoWorkstartWorkerReceiverThreadWorkerReceiver[myid=1]doWork ?
点评
启动过程做了几件事:1、从事务日志和快照日志恢复数据到内存数据库 2、启动对外服务端口
3、启动选举端口和相关线程,开始投票选举leader的过程 数据恢复是这样的:首先将最近的一次完好无损的快照文件全量解析到内存数据库,然后从事务日志中比快照文件生成的事务id的下一个事务id开始,依次应用事务到内存数据库当中,并加载currentEpoch以及lastZxid。
5. zk leader选举
ThreadNIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181followLeaderZKDatabaseNIOServerCnxnFactorydoWorkNIOServerCnxndoIOobserveLeaderServerSocketLeaderZooKeeperServerFollowerZooKeeperServerObserverZooKeeperServerloadDataBasedoAcceptSocketChannelAcceptZkServerleadLeaderFollowerObserverServerSocketChannelCreatemakeLeaderCreatemakeFollowerCreatemakeObserverQuorumPeerStartThreadwhf1/192.168.137.10:3888leaderingFollowingobserveringstartLeaderElectionCreate/startQuorumCnxManager.ListenerdoWorkServerSocket(port:3888)建立连接、传递sidSocketOBSERVING/FOLLOWING/LEADING状态createThreadQuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181AcceptSocketdoWorkCreatecurrentVote(myid,lastZxid,currentEpoch)recvQueue无异议Poll for 200msNotificationNotificationNotificationNotificationNotificationRecv Votes setSid-->leader,zxid,epochSid-->leader,zxid,epochSid-->leader,zxid,epochLOOKING状态FastLeaderElectionCreate/initreceiveConnectionWait and poll toPredicate是否多数一致Messengercreate只允许id大的去连id小的否,继续循环是termPredicatesenderWorkerMapQuorumCnxManager③投票来自looking③投票来自leader/followerlookForLeaderstartstartSidSidSendWorkerSendWorkerSendWorkerRecvWorker1、判断leader是否认为自己是leaderCheckLeaderooePredicate①③update②WorkerSenderWorkerReceiverSidsendNotifications用于创建toSendVotepollThreadWorkerSender[myid=1]ThreadWorkerReceiver[myid=1]Create and startThreadSendWorker:IddoWorkdoWorkdoWorkSocketSend(ByteBuffer)VotingViewSID1offer循环SID1SID1QuorumServerQuorumServerQuorumServer①POLLinitConnectionOfferSocket建立连接比对sid大小ServerSocket(port:3888)recvQueue②ProcessNotificationNotificationNotificationNotificationNotificationAddPollPOLLToSendsendqueueToSendToSendToSendToSendToSendQuorumCnxManager.sendQueueZoom InByteBufferByteBufferByteBufferByteBufferByteBufferByteBufferqueueSendMapSidSidZoom InSidQueueQueueQueueIs To SelftoSend否/add是/addQuorumCnxManagerMessageQuorumCnxManager.recvQueue for selfThreadRecvWorker:1QuorumCnxManagerdoWorkMessageMessageMessageMessageMessageZoom InToSend(leaderId,Zxid,logicClock,sid,state)ADD
? 点评: ? 选票格式:
(proposedLeaderId,proposedZxid,logicalclock,proposedEpoch) 初始投票是
(自己的serverid,自己的lastZxid,自己的logicalClock,自己的currentEpoch)
说明:Logicalclock是选举次数,自进程启动以来经历的第几次选举,为避免因网络过慢导致上轮选举造成影响,故logicclock要达成一致,以最大的那个优先 ? PK规则:先比较epoch,epoch相同的话比较zxid,zxid再相同的话比较serverid,
值大的那个获胜,参与投票的server如果发现接收到的投票比自己大,那么会更新自己的投票再发送出去。 ? 选举规则
1、 所有处于looking状态的server都向其它所有server发送投票,初始投票选自己。 2、 接收来自其它server的投票,接收不到就继续发送本地投票,如果PK失败则更新本
地投票重新发出
3、 当收集的投票满足条件--->包括本地投票在内的quorum数量的投票都达成一致,等
待200ms无异议时,当接收的投票来自于leadering或者following状态时还需要判断leader是否处于leadering状态,以避免建立连接不成重新进入looking状态
4、 Follower和leader进入各自的角色,leader启动sync端口,follower向
leader建立连接,follower向leader注册自己,leader发现follower不足够会重新进入选举阶段。
? 例子说明
如上图,假设epoch都是1,s3的sid=3,zxid为5,s2的sid=2,zxid为5,s3的sid=1,zxid=6.
s3,s2收到指向s1的投票后,修改投票重新发出,最终quorum数量达到投票一致,一致选举S1.
? Falsely Election
①还是上图的例子,s3,s2互发投票消息,由于网络原因都没有接到s1的投票,s3,s2判断投票多数达成一致都是选s3做leader,休眠200ms,s1投票依旧没有到来,于是s3成为leaer,s2成为follower。等s1投票消息到达s2,s3时epoch已经增1变成2了,s1将更新自己的投票选举s3.s1成为follower,在s1与leade数据同步时,s1将删除epoch=1时的事务6,造成数据丢失。
②还是s1网络故障,s1的投票没有达到s3,在200ms内到达了s2,s2将更新投票投s1,s1投票选s2,s3在休眠200ms后s1和s2新的投票消息仍然没有到达s3,s3判断s2为leader,试图与s2建立连接,但是s2认为s1是leader,于是不开启sync端口,s3建立连接超时,重新回到LOOKING状态。
finalWait控制在200ms以内,快速选举,这也就是Fast Leader Election命名的初衷吧
? 故障恢复
一个有2N+1个实例组成的zookeeper集群,最大能允许N个实例出现故障,超过N个就会造成服务不可用。那什么情况下会使得集群进入leader选举状态呢?
1、leader出现故障:当leader出现故障时所有的 Follower会进入LOOKING状态重新进行投票选举
2、当集群中有超过N个实例出现故障,这时leader将接收不到足够quorum数量的PING响应,这时leader 关闭同步端口,断开所有的learner连接,使得整个集群都进入LOOKING状态
3、当选举成功leader角色和Follower角色都已经建立之后,如果在Follower注册、Follower与leader建立连接、同步数据成功的个数,不足quorum数量,都会导致重新进入LOOKING状态重新进行leader选举。
4、learner和leader之间连接是BIO的,learner一直做阻塞读和阻塞写的循环,一旦发现读超时则退回到LOOKING状态重新寻找leader
6. 数据同步
1、将同步过来的快照应用到内存数据库2、将接收的proposal和commit放到队列缓存起来3、接收到newleader后,生成一个快照文件,更新currentEpoch4、响应ack5、接收updateTodate,关联ZookeeperSever为开启对外服务准备sendsocketPINGACKREVALIDATEPacket类型ReadPackage&processpackageREQUESTFOLLOWERINFO1、发送Followerinfo2、接收leaderinfo改变本地acceptedEpoch3、响应ACKEPOCHsend①QuorumPeer[myid=1]connectToLeaderLEADERINFO遇到异常ACKEPOCHrecvsendrecvsendsendsendsendsendrecvWaitsendNEWLEADERrecvWaitcreate/startSocketLearnerHandler-/192.168.137.10:51850Sender-/192.168.137.10:51850sendpackagerecvsendSocketrecvrecvrecvsyncWithLeader④PINGPROPOSALrecvrecvsendrecvREVALIDATESYNCsenddoworkSHUTDOWNdoworkqueuedPacketspollQuorumPacketQuorumPacketQuorumPacketSNAP/DIFF/TRUNCZKDATABASE DATAQuorumPeerFOLLOWERING状态FollowerfollowLeader②③registerWithLeaderPROPOSALaddLearnerHandlerLeaderLearnersLearnerHandlerSocketLearnerHandleracceptSocketacceptdoworkServerSocketZkDataBasehzxidLearnerHandlerCOMMITFollowerZookeeperServerdoWorkReadPacket&processPacketPacket类型读超时等异常StartupSendAckRequestProcessorQuorumPeercreate/startcreate/startsetupRequestProcessorsLOOKING状态UPTODATEWaitThread 2888ACKloadDataBasesetloadData请求处理器SyncRequestProcessorcreate/startcreate/startFollowerRequestProcessorACKLearnerCnxAcceptorQuorumPeer[myid=2]LeaderZooKeeperServer①startnextCommitProcessornextFinalRequestProcessorWait quorum packageFor initLimit*ticktime②leadLeaderkillDeadsessions超时④LOOKING状态QuorumPeerDoworkLEADERING状态makeLeader重新选举startZkServer心跳检测LeaderZooKeeperServer遍历LearnersLearnerHandlerLearnerHandlerLearnerHandlerPING每隔ticktime/2检测一次,发次pingLearnerHandlerStartupCheck1、在syncLimt*ticktime内到响应的serverid将加入set2、每隔一个ticktime判断set是否足够覆盖大多数server3、否则shutdown Zookeeper进入looking状态startstartSessionTrackerImplinitializestartPrepRequestProcessorstartstartnextProposalRequestProcessornextCommitProcessornextLeader.ToBeAppliedRequestProcessornextFinalRequestProcessor
?
点评
初始化和数据同步阶段主要完成这么几件事 Leader
1、 对于leader,启动同步端口,为每个learner分配一股learnerHandler和
Sender线程
2、 等待超过集群机器半数的follower注册进入forwarding列表,
等待超过机器机器半数的follower接收到同步数据,完成数据同步。 3、 开启对外服务,启动处理事务的processor
Follower
1、 对于follower,建立与leader同步端口的连接 2、 同步epoch,向leader注册自己,完成数据同步 3、 启动处理事务的processor
相关推荐: