Zookeeper-Server端请求处理链

Zookeeper Server端对接收到的Client端请求,以及Leader接收到的Follower/Observer请求,都会以"处理链"的方式"分工/逐步"处理,这是一种良好的设计模式..不过事实上Zookeeper源码中,这部分代码写的确实比较"纠结"..

一. Folower端:

Follower与Client的通讯入口为ServerCnxn.doIO()方法,Follower与Leader的通讯操作入口Follower.followLeader()方法.下文中提到的请求,即为"Request"对象(将用户操作封装之后的对象)

FollowerZookeeperServer实例就是Follower为Client请求服务的对象.任何一个请求都将会依次经过如下"处理器"或过程:

1)   ZookeeperServer.processPacket(Request)方法开始处理请求,如果请求符合要求,将会被交付给"处理器".

2)   FollowerRequestProcessor(线程):作为Follower的首个"处理器",将Client请求队列化,因为其本身即为线程,则在线程run方法中,则从队列中逐个take出请求,并交付给"下一个处理器"(nextProcessor),对于write操作(create,delete,setData...)同时也将请求转发给Leader一份.

3)   Follower.followLeader()中,如果来自Leader的消息类型为"proposal",则将此消息交付给SyncRequestProcessor处理器.

4)   SyncRequestProcessor(线程):对于2)处理器转发给Leader的请求,Leader会经过处理后给所有的Followers发送"Proposal"(提议),Proposal中将会包含此次请求的zxid,cxid等.SyncRequestProcessor主要做了一件事情:将请求写入txnLog中.如果事务日志写入成功,则将请求转发给5)处理器

5)   SendAckRequestProcessor:因为在3)中,Follower已经接收到"提议",则在此处理器中,直接回馈Leader一个ACK包,按照惯例,此包仍然会包括zxid(此zxid来自proposal).

6)   Follower.followLeader()中,如果来自Leader的消息类型为"commit",则将此消息交付给CommitProcessor处理器.此处进行了严格的请求次序控制,如果发现乱序情况,则直接导致系统退出(乱序的特征:pendingTxns.element().zxid,其中pendingTxns是个队列,表示proposal队列,即commit的顺序必须和proposal的顺序一致)

7)   CommitProcessor(线程): 对于来自Leader的commit请求,会被队列化,那么在CommitProcessor线程的run方法中,则会逐个处理"commit"请求.此处理器很简单,它的特点就是让请求处理"逐个进行"传递给下一个处理器.

8)   FinalRequestProcessor:将数据变更操作持久化,操作ZKDatabase,如果有必要,则触发相应的watches.

二.Observer端: 和Follower很像,但是它没有SendAckRequestProcessor,当接收到commit类型的请求时,直接提交就行.它不参与proposal和ack.

二.Leader端:

Leader端和Follower端在交互上形成对应.Leader与Follower通讯入口为LearnerHandler.run();

1)   对于Follower转发给Leader的write操作,请求类型为Leader.REQUEST,则触发此请求添加到请求队列中,此后将有"处理器链"依次处理.

2)   PreRequestProcessor(线程):将请求队列化,是请求的预处理阶段,因为Leader可以断言所有的write操作必将带来额外的影响,比如节点的创建会影响其父节点的version变更,比如Sequential节点创建需要计算序列号码等.此处理器并没有做实际的工作,主要作用就是生成一个zxid,并交付给请求....继续下一个处理器.

3)   ProposalRequestProcessor:将请求封装成一个"Proposal"依次发送给所有的Follower,发送操作也是有LearnerHandler来做...此后,将有将请求交付给SyncRequestProcessor.

4)   SyncRequestProcessor(线程):和Follower一样,记录txnLog.

5)   AckRequestProcessor:是Leader对请求的补充操作,Leader对请求直接做"ACK"操作,意味着任何一个Proposal,Leader都会立即提交ACK.

6)   Leader此时将会获得Follower交付的ACK请求,将会直接使用leader.processAck(serverId,zxid..)处理,此方法的主要作用就是,对请求队列中亟待"commit"的请求进行确认,直到"大多数"Follower都对某个请求进行了ACK,此请求才会被commit;如果对一个已经commited的请求进行确认(即当前ack请求的zxid小于先已提交的zxid),将会忽略(下一个处理器,将会告知其立即提交)...

7)   CommitProcessor(线程):如果LearnerHandler接收到ACK请求时,都会执行Leader.commit()方法,此方法的最要作用就是针对"多数派"的请求,则向Follower/Observer发送Commit请求;此后,把此请求交付给下一个处理器ToBeAppliedRequestProcessor.

8)   ToBeAppliedRequestProcessor:根据命名,我们也能知道,此处理器就是在"即将实施变更"之前,做一些额外的工作,在向所有的Follower发送Commit之前,Leader特意把此请求添加一个队列中(为了防止,在最后时机发送乱序情况,比如发送commit时遇到异常),如果"请求"到达ToBeAppliedRequestProcessor,说明当前commit请求被如期发送给了所有的所有的Follower(事实上有可能正在发送),那么ToBeAppliedRequestProcessor所做的就是把队列头部的Commit移除,并继续传递给下一个处理器.

9)   FinalRequestProcessor:和Follower一样,在本地ZKDatabase中持久化此变更请求.

这就是Follower和Leader进行的"二阶段"提交和事务控制的全过程,不过因为IO操作本身就存在很多"异常可能";那么Follower和Leader所做的,就是控制好请求被执行的顺序,也就是确保事务性和全局数据一致性;如果Follower发现乱序或者IO异常,唯一能做的,就是重新和Leader同步;Leader能做的就是阻塞后续请求或者发起选举.

猜你喜欢

转载自shift-alt-ctrl.iteye.com/blog/1849545
今日推荐