#0 系列目录#node
Zookeeper系列服务器
Zookeeper源码日志
Zookeeper应用
#1 各服务器角色的请求处理器链# 先介绍下Leader、Follower、Observer服务器的请求处理器链 ##1.1 Leader服务器## PrepRequestProcessor-》ProposalRequestProcessor-》CommitProcessor-》ToBeAppliedRequestProcessor-》FinalRequestProcessor
ProposalRequestProcessor-》SyncRequestProcessor-》AckRequestProcessor
下面分别一一介绍
PrepRequestProcessor:首先为请求分配请求号zxid
,而后对客户端用户发送过来的请求或者Follower、Observer转发过来的请求进行事务的区分
,若是是事务请求则建立出事务请求头,后面的请求处理器就是依据是否有事务请求头来判断该请求是不是事务请求。同时进行一些验证工做
:如session是否过时,验证权限等操做。
ProposalRequestProcessor:主要对事务请求,向全部的Follower服务器发起一个议案
,同时触发SyncRequestProcessor
对事务请求的记录。
SyncRequestProcessor:对事务请求记录到事务日志文件中
,记录完成后触发AckRequestProcessor
。
AckRequestProcessor:对于上述议案,Leader也是投票的一份子,因此也要进行投票响应,只需执行下Leader的响应方法便可
。而其余Follower服务器的投票响应则是须要向Leader发送一个Leader.ACK响应
,Leader接收到后,一样去执行Leader的响应方法。
在Leader的响应方法每执行一次,就会判断是否已通过半机器响应了,若是过半,则Leader向全部的Follower和Observer发送Leader.COMMIT请求
,同时Leader也向本身的CommitProcessor中提交该事务请求,即该事务请求是经过过半机器认同的,须要被提交的事务请求。
CommitProcessor:对于已经被过半机器认同的请求交给下一个处理器处理
。而那些尚未被过半机器认同的,则处于阻塞状态。
ToBeAppliedRequestProcessor:负责将请求交给下一个处理器FinalRequestProcessor
,处理完毕后表示已经完成该项议案,而后就删除了该项议案
FinalRequestProcessor:最后一个请求处理器
。对于事务请求,执行事务的具体操做
,如增删改node、createSession、closeSession等。对于非事务操做如获取数据等,从DataTree中获取相应的数据
。最终返回数据给客户端。
##1.2 Follower服务器## FollowerRequestProcessor-》CommitProcessor-》FinalRequestProcessor
SyncRequestProcessor-》SendAckRequestProcessor
FollowerRequestProcessor:首先将请求交给下一个处理器即CommitProcessor处理器
,若是该请求是事务请求或者前面有事务请求在等待处理,则该请求会被阻塞
。若是是事务请求,交给CommitProcessor处理器以后,又立马将该请求转发给Leader
,即事务请求必需要通过Leader,而后Leader又会把该事务请求封装成一个议案发给各个Follower服务器进行投票
,各个Follower服务器接收到Leader发送过来议案后,首先要把这个议案请求记录到事务日志中,即调用SyncRequestProcessor来处理
。
CommitProcessor:一旦是事务请求,就须要等待该事务请求被过半数承认
,接收到Leader的Leader.COMMIT请求,才会继续走下去。
SyncRequestProcessor:把Leader发送过来的议案记录到事务日志中
,而后交给下一个处理器SendAckRequestProcessor
。
SendAckRequestProcessor:当日志记录完成以后,须要给Leader发送一个Leader.ACK响应,表示已经成功记录在案
。
Leader开始统计Follower发送过来的响应,一旦有过半机器发送过来响应,则认为该事务能够提交了
。而后Leader就向全部的Follower发送Leader.COMMIT请求
,带上以前的请求号,向全部的Observer发送Leader.INFORM请求,带上以前的整个请求内容,由于Follower在前面已经接收到了该请求,而Observer则没有接收到该请求,因此要对Observer带上整个请求内容
。
Follower接收到Leader发送过来的Leader.COMMIT请求以后,根据带过来的请求号,找到真个请求对象,而后放到Follower的CommitProcessor中,使之继续走下去,交给FinalRequestProcessor
。
##1.3 Observer服务器## ObserverRequestProcessor-》CommitProcessor-》FinalRequestProcessor
SyncRequestProcessor
其中上述SyncRequestProcessor是经过配置zookeeper.observer.syncEnabled系统属性的true or false来决定是否须要这个处理器
,默认true
ObserverRequestProcessor:和FollowerRequestProcessor功能彻底同样,只是参数不同,能够抽象出来的。
因此咱们看到Observer服务器和Follower服务器的处理器链基本差很少。不一样之处就是Follower服务器还有一个SendAckRequestProcessor,向Leader发送投票反馈。而Observer不参与投票,则不须要这个处理器
。
以上就大体说完了各个服务器角色的请求处理器链,下面就结合具体的请求案例,再来捋一下整个过程。
#2 链接Leader创建session关联的过程和session不断激活的过程# 这里链接的服务器以Leader为例,先说创建session关联的过程,以后再说session不断激活的过程 ##2.1 创建session关联的过程## 这就须要从用户建立ZooKeeper对象开始提及。
用户建立ZooKeeper对象,内部建立出ClientCnxn
,能够简单想象成ZooKeeper对象的内部管家,ClientCnxn有两个主要的线程SendThread和EventThread
SendThread负责与服务器端的通讯,EventThread负责事件的通知
。
1.1 SendThread启动以后,就从建立ZooKeeper对象的地址列表(被随机打乱了),取出一个服务器地址进行tcp链接操做
1.2 当tcp链接成功以后,就须要和服务器端创建session关联
。依托tcp链接,向服务器端发送ConnectRequest请求
,会把建立ZooKeeper对象时指定的sessionTimeout时间带上
服务器端会给客户端建立一个ServerCnxn,专门负责与该客户端的通讯
2.1 当客户端第一次发送ConnectRequest请求到ServerCnxn中
,ServerCnxn首先会对tcp链接传递过来的数据序列化成ConnectRequest
,拿到客户端传递的sessionTimeout时间,因为服务器端在启动的时候指定了maxSessionTimeout、minSessionTimeout(即便没有指定,也会使用默认的),要求客户端传递过来的sessionTimeout时间必须在此二者之间
,不符合要求的分别取对应的最大值或者最小值
2.2 而后就使用Leader服务器的SessionTracker(session管理器)根据上面协商后的sessionTimeout时间,分配出sessionId,建立出session
。
2.3 根据分配的sessionId和刚才的ServerCnxn建立出一个请求,类型为OpCode.createSession,将该请求提交到Leader的请求处理器链上
。
2.4 首先遇到的是PrepRequestProcessor处理器
,认为OpCode.createSession请求是一个事务请求,就建立了一个事务请求体,再次执行了session的添加操做
,主要是做用于从Follower等转发过来的建立session的请求。放心不会进行重复添加的,里面进行来判断的。
2.5 PrepRequestProcessor处理器执行完毕,交给下一个处理器ProposalRequestProcessor
。ProposalRequestProcessor处理器将该建立session的请求立马交给了下一个处理器CommitProcessor的处理队列中(该请求被阻塞在那)
,而后又立马返回将该请求封装成一个议案,发送给全部的Follower服务器
。
2.6 Follower服务器接收到Leader发过来的议案后,使用SyncRequestProcessor将该请求即建立session的请求记录到事务日志中
,而后交给Follower的下一个处理器SendAckRequestProcessor,使用该处理向Leader发送Leader.ACK反馈
。
2.7 每收到Follower的一次Leader.ACK反馈,就要统计下是否已通过半数了
,若是过半数,则Leader向全部的Follower发送Leader.COMMIT命令,带上以前的请求号,向全部的Observer发送Leader.INFORM命令,须要带上以前的整个请求内容
。同时Leader也向本身的CommitProcessor提交建立session的请求,CommitProcessor拿到该请求后,再也不阻塞,继续走向下一个处理器ToBeAppliedRequestProcessor
。
2.8 ToBeAppliedRequestProcessor将建立session的请求先交给下一个处理器FinalRequestProcessor处理
,当FinalRequestProcessor处理完成以后,删除以前提出的针对该请求的议案
2.9 FinalRequestProcessor针对建立session的请求,会使用SessionTracker再次执行建立(不会重复的,内部进行了判断,一旦已经有了sessionId对应的session则只须要查看是否过时,若是没有过时则从新激活session,即从新计算session的过时时间)
。而后就Leader从request中取出ServerCnxn,开始准备向客户端发送响应了
。响应内容是sessionId、根据sessionId计算出来的密码、协商后的sessionTimeout时间
2.10 虽然上述Leader已经对客户端进行了响应,可是其余Follower和Observer接收到Leader发送的Leader.COMMIT命令和Leader.INFORM命令,他们接收到上述命令以后,也都从CommitProcessor中走出来了,再也不阻塞在那里
,走向了FinalRequestProcessor。仍然使用SessionTracker再次执行建立session的操做,可是Follower和Observer中的SessionTracker实现是LearnerSessionTracker
,而Leader中的SessionTracker是SessionTrackerImpl。
session已经在Leader内部建立出来了,其余的Follower、Observer仅仅是保存下sessionId和sessionTimeout时间,并无完整的session对象
。
所谓的session在全部机器上共享,其实就是Leader中保存着全部的session信息,并负责检查session的过时
。其余机器只负责简单保存下sessionId以及对应的sessionTimeout时间
,对于session问题下面再详细说明。
FinalRequestProcessor执行完session添加以后,也从request中取出对应的ServerCnxn
,固然为null,而后就不执行任何操做。只有客户端所链接的那台服务器才会有ServerCnxn
。
首先判断服务器端返回的sessionTimeout时间是否小于等于0
若是小于等于0则表示session建立失败。向EventThread中发送两个事件,第一个事件是KeeperState.Expired类型的事件,即session过时事件
。第二个事件是eventOfDeath死亡事件
,当EventThread收到eventOfDeath事件后,就会结束EventThread线程循环,EventThread线程走向死亡,即ZooKeeper对象再也不可用。
若是大于0则表示建立session成功。向EventThread中发送一个KeeperState.SyncConnected事件
。
##2.2 session不断激活的过程## 上面说完了创建session关联的过程,下面就说说session是如何不断的激活的。
由于服务器端,Leader服务器的SessionTrackerImpl,会每隔tickTime时间就会执行一次session过时检查
,若是session没有及时激活的话,就会过时,就会被SessionTrackerImpl清理掉,对应的客户端ZooKeeper对象就不可用了。
客户端的SendThread线程在循环过程当中,不断的向服务器端发送Ping请求
。操做类型为OpCode.ping对于发送频率,先说明下。
客户端向服务器端最后一次发送请求的时间
lastSend时间到当前时间的间隔
取值是2/3的sessionTimeout时间
先将请求封装成一个Request对象,而后提交该请求到Leader的请求处理器链
2.1 在交给请求处理器以前,进行了session的激活操做。
SessionTrackerImpl对于session超时检查,是进行的分桶策略
。以tickTime的整数倍的时间点就是一个桶,存放着在该时间点过时的session
。
session被激活的过程就是从某个tickTime的整数倍的时间点对应的桶中移到后面时间点对应的桶中
SessionTrackerImpl会每隔tickTime时间就会执行一次session过时检查
,有了分桶策略就比较方便了,不用遍历每一个session执行检查,只须要查看当前时间点对应的桶中是否含有session,若是有则表示该session没有被及时激活,须要进行过时操做
。
session的激活就是先检查当前session是否过时,若是没有过时,则从新计算session的过时时间,计算方式就是当前时间加上sessionTimeout时间而后取一个tickTime的整数值,即选择了后面的一个桶进行存放该session
。
2.2 首先是Leader的PrepRequestProcessor处理器:发现该请求是Ping请求,不会建立事务请求体,只会检查下session是否过时
。而后交给下一个处理器ProposalRequestProcessor。
2.3 ProposalRequestProcessor对于非事务请求也仅仅是直接交给下一个处理器CommitProcessor
。
2.4 CommitProcessor中若是没有正在等待处理的事务请求,则会直接交给下一个处理器ToBeAppliedRequestProcessor
。若是有正在被处理的事务请求,则也须要进行等待,感受这里不是太合理,Ping类型的请求,应该直接经过,不通过任何等待的
。
2.5 ToBeAppliedRequestProcessor也没有作什么处理,直接交给下一个处理器FinalRequestProcessor
。
2.6 FinalRequestProcessor针对ping请求,直接进行响应便可
。
能够看到当客户端链接的是Leader服务器时
,session的不断激活就是经过客户端不断发送Ping请求给Leader服务器端,从新计算session过时时间达到激活session的目的
。能够看到Follower、Observer都不参与此过程
,然而当客户端链接的不是Leader服务器端,就不同了,过程就没这么简单了。后面详细说明。
##2.3 session过时过程## 一旦Leader发现某个session过时了,会先从Leader中删除该session,而后建立一个OpCode.closeSession请求,提交到Leader的请求处理器链
。
首先是Leader的PrepRequestProcessor处理器,发现session过时是一个事务请求,建立出事务请求头
。而后设置该session的isClosing属性为true,而后交给下一个处理器ProposalRequestProcessor
。
ProposalRequestProcessor处理器先把该请求交给下一个处理器CommitProcessor
,因为该请求是事务请求,则针对该请求提出一个议案,发给全部的Follower进行投票,其实所谓的投票就是Follower记录事务请求的过程,记录成功并发送响应给Leader,就算是一次成功投票
。一旦过半数的Follower进行了反馈,Leader就认为这次事务请求能够被提交了
。而后向全部的Follower发送Leader.COMMIT请求
,向全部的Observer发送Leader.INFORM请求
。向Leader的CommitProcessor处理器的提交队列中发送这个closeSession请求
。使之继续往下一个处理器走,下一个处理器即ToBeAppliedRequestProcessor
ToBeAppliedRequestProcessor也没有作什么处理,直接交给下一个处理器FinalRequestProcessor
在FinalRequestProcessor中对closeSession请求会作如下操做:
首先删除这个session建立的全部临时节点,并触发临时节点删除的事件
,类型为EventType.NodeDeleted,同时触发父节点的children变化的事件,类型为EventType.NodeChildrenChanged。
其次再次从sessionTracker中将该session删除
最后关闭使用该session建立的ServerCnxn,即断开了与客户端的链接
,至此,Leader就完成了closeSession的整个过程
仍然是到FinalRequestProcessor执行上述一样的操做
。至此,session在整个集群中就完全被删除了。当客户端链接的是Leader服务器,创建session关联和session激活、session过时过程比较简单
。一旦是客户端链接的是Follower或者Observer的时候,过程就稍微多了一些。
##2.4 Leader处理流程图##
#3 链接Follower创建session关联的过程和session不断激活的过程# 当客户端链接的是Follower的话,和上面的状况稍有差异。下面的部份内容和上面有不少重复的地方,为了方便观看,重复的部分直接复制过来了,同时要注意不一样的地方。 ##3.1 创建session关联的过程## 这就须要从用户建立ZooKeeper对象开始提及。
内部建立出ClientCnxn
,能够简单想象成ZooKeeper对象的内部管家,ClientCnxn有两个主要的线程SendThread和EventThread
SendThread负责与服务器端的通讯,EventThread负责事件的通知
。
1.1 SendThread启动以后,就从建立ZooKeeper对象的地址列表(被随机打乱了),取出一个服务器地址进行tcp链接操做
。
1.2 当tcp链接链接成功以后,就须要和服务器端创建session关联。依托tcp链接,向服务器端发送ConnectRequest请求,会把建立ZooKeeper对象时指定的sessionTimeout时间带上
。
服务器端会给客户端建立一个ServerCnxn,专门负责与该客户端的通讯
2.1 当客户端第一次发送ConnectRequest请求到ServerCnxn中,ServerCnxn首先会对tcp链接传递过来的数据序列化成ConnectRequest
,拿到客户端传递的sessionTimeout时间,因为服务器端在启动的时候指定了maxSessionTimeout、minSessionTimeout(即便没有指定,也会使用默认的),要求客户端传递过来的sessionTimeout时间必须在此二者之间
,不符合要求的分别取对应的最大值或者最小值
2.2 而后就使用Follower服务器的SessionTracker(session管理器)根据上面协商后的sessionTimeout时间,分配出sessionId
。
Leader服务器使用的SessionTracker是SessionTrackerImpl
,而Follower使用的SessionTracker是LearnerSessionTracker
。二者的区别以下:
SessionTrackerImpl:不只分配sessionId,还负责建立session对象,维护session对象,开启线程不断检查session是否过时
。
LearnerSessionTracker:仅仅分配sessionId,只保存sessionId,没有session对象。session对象的建立都是在Leader的SessionTrackerImpl中建立的
。
2.3 根据分配的sessionId和刚才的ServerCnxn建立出一个请求,类型为OpCode.createSession,将该请求提交到Follower的请求处理器链上
。
2.4 首先遇到的是Follower服务器的FollowerRequestProcessor处理器
,它先将该请求交给下一个处理器CommitProcessor,而后会阻塞
。因为建立session请求是事务请求,则这个Follower会把该请求转发给Leader服务器
。
Leader服务器为上述Follower服务器分配的LearnerHandler会收到来自Follower服务器的上述建立session的请求,LearnerHandler把该请求交给了Leader请求处理器链来处理了
。3.1 首先遇到的是PrepRequestProcessor处理器,认为OpCode.createSession请求是一个事务请求,就建立了一个事务请求体
,根据请求传递过来的sessionId和sessionTimeout时间,使用Leader服务器的SessionTrackerImpl建立出了session,保存来起来
。而后就交给了下一个处理器ProposalRequestProcessor来处理
。
3.2 ProposalRequestProcessor处理器将该建立session的请求立马交给了下一个处理器CommitProcessor的处理队列中(该请求被阻塞在那)
,而后又立马返回**将该请求封装成一个议案,发送给全部的Follower服务器
**。
3.3 Follower服务器接收到Leader发过来的议案后,使用SyncRequestProcessor将该请求即建立session的请求记录到事务日志中
,而后交给Follower的下一个处理器SendAckRequestProcessor
,使用该处理向Leader发送Leader.ACK反馈
3.4 每收到Follower的一次Leader.ACK反馈,就要统计下是否已通过半数了
,若是过半数,则Leader向全部的Follower发送Leader.COMMIT命令,带上以前的请求号
,向全部的Observer发送Leader.INFORM命令,须要带上以前的整个请求内容
。同时Leader也向本身的CommitProcessor提交建立session的请求,CommitProcessor拿到该请求后,再也不阻塞,继续走向下一个处理器ToBeAppliedRequestProcessor
。
3.5 ToBeAppliedRequestProcessor将建立session的请求先交给下一个处理器FinalRequestProcessor处理
,当FinalRequestProcessor处理完成以后,删除以前提出的针对该请求的议案。
3.6 FinalRequestProcessor针对建立session的请求,会使用SessionTracker再次执行建立(不会重复的,内部进行了判断,一旦已经有了sessionId对应的session则只须要查看是否过时,若是没有过时则从新激活session,即从新计算session的过时时间)
。而后就Leader从request中取出ServerCnxn,发现为null(只有客户端链接的那台服务器的才会有对应的ServerCnxn),就什么也不执行
。至此Leader的任务已经完成。
他们分别接收到Leader的Leader.COMMIT、Leader.INFORM命令以后
,就会在他们的CommitProcessor处理器的提交队列中接收到建立session的请求,而后交给下一个处理器即FinalRequestProcessor处理器来执行建立session的具体内容
Follower和Observer服务器都使用LearnerSessionTracker记录下这个sessionId和对应的sessionTimeout时间
。至此,全部的服务器上都保存了这个建立的sessionId了。
而后其余的一些Observer和Follower从request中取出ServerCnxn,发现都为null,什么也不操做。只有客户端链接的这台Follower取出的ServerCnxn是有值的,须要给客户端反馈建立session的响应了
。
响应内容就是sessionTimeout、sessionId、根据sessionId计算出的密码
。
若是小于等于0则表示session建立失败。向EventThread中发送两个事件,第一个事件是KeeperState.Expired类型的事件,即session过时事件。第二个事件是eventOfDeath死亡事件,当EventThread收到eventOfDeath事件后,就会结束EventThread线程循环,EventThread线程走向死亡,即ZooKeeper对象再也不可用。
若是大于0则表示建立session成功。向EventThread中发送一个KeeperState.SyncConnected事件
##3.2 session不断激活的过程## 客户端仍是同样,按期向链接的服务器这里是Follower服务器,发送Ping请求
对于发送频率,先说明下。
为该客户端分配的ServerCnxn接收到客户端的Ping请求后,先将请求封装成一个Request对象,而后提交该请求到Follower的请求处理器链
。2.1 在交给请求处理器以前,进行了session的激活操做
。
LearnerSessionTracker对session的激活,仅仅是把该sessionId和sessionTimeout时间放到另外一个HashMap<Long, Integer> touchTable结构中,仅此而已
。其实咱们想要达到的目的是:可以在Leader服务器上进行真正的session激活,否则的话,Leader服务器在不断检查session过时,一旦没有及时激活就会删除该session的
。先提出一个疑问:客户端的Ping请求如何来激活Leader服务器上保存的session呢?
。
2.2 首先是Follower的FollowerRequestProcessor处理器:直接把该请求交给下一个处理器CommitProcessor
,因为Ping请求并非事务请求,则不会把该请求转发给Leader服务器。
2.3 CommitProcessor中若是没有正在等待处理的事务请求,则会直接交给下一个处理器FinalRequestProcessor
。若是有正在被处理的事务请求,则也须要进行等待,感受这里不是太合理,Ping类型的请求,应该直接经过,不通过任何等待的。
2.4 FinalRequestProcessor针对ping请求,直接发送进行响应
。
至此整个客户端的Ping请求就处理完成了。上面的疑问还没解决呢?此次Ping请求的目的还没达到呢?
还有一个过程以下:
Leader会在一个while循环中,经过循环遍历全部的LearnerHandler不断的向全部的其余服务器发送Ping请求,用于检测Leader和其余服务器之间的正常链接,一旦超时没有获得及时的反馈,则就会删除该LearnerHandler
。一旦LearnerHandler的数量小于最初总数量的一半,则认为该Leader该下台了,须要从新选举Leader了。
Follower和Observer接收到Leader的Ping请求以后,就会把LearnerSessionTracker中的touchTable(里面的这些sessionId都是须要从新被激活的)所有传递给Leader
。
Leader接收到这些须要被激活的session后,一个一个挨个的从新计算了session的超时时间
。至此,客户端的Ping请求终于达到效果了。
##3.3 Follower处理流程图##
#4 Leader/Follower之间的消息传递#