Zookeeper-Server数据同步与L-F交互

Zookeeper服务端初始化过程(三):数据恢复与同步


Zookeeper-Server数据同步与L-F交互_第1张图片

 


server类型(ServerType

client端服务类

server端服务类

Leader(new)

LeaderZookeeperServer

Leader

Follower

FollowerZookeeperServer

FolloweràLearner

Observer

ObserverZookeeperServer

ObserveràLearner

 

  1. 在选举中,即当server的状态为LOOKING是,将会实例化一个ReadOnlyZookeeperServer来临时为client服务(如果server开启了readonly模式)。
  2. Leader是否可以直接服务于Client,取决于配置文件中“zookeeper.leaderServes”参数,即leader是否支持client服务.(当Leader不支持时,似乎是以异常的方式结束client链接,导致client被强制和其他Follower链接)。
  3. Client与server之间是通过NIO方式(长连接)进行通信,server端为单线程处理;每个socket链接对于server端而言,都有一个ServerCnxn类来维护,所有的请求ServerCnxnFatory之后交付给相应的ServerCnxn,最终请求packet经过zookeeperServer实例的各个processor处理。(“请求”全序性)
  4. Follower与Leader之间的通信,有些不同;Leader会维护每个Follower之间的链接,当然也是长连接,不过并没有使用NIO,而是普通的socket通信(同步数据交互,而非异步);Leader会为每个链接的Follower/Observer创建一个LearnerHandler线程,用于处理2者之间的数据交互,LearnerHandler类持有Leader实例;所以对于Leader而言,是多线程处理Follower/Observer的数据交互。

Zookeeper-Server数据同步与L-F交互_第2张图片
  1. LearnerCnxAcceptor,一个非常简单的线程,为Leader的内部类。它的主要作用是accept来自Follower/Observer的链接(侦听followingPort),为每个socket绑定一个LearnerHandler处理器线程,此线程持有socket句柄。此线程处理来自Follower/Observer的请求,并发送Leader的请求给Follower/Observer.
  2. 选举之前epoch来自server当前的zxid,N轮选举成功之后,最终的最大的epoch将作为最终的epoch;zxid的前32位为epoch,所以每次选举之后,leader的职责就是重新计算zxid,以免发生重复;在lead正式服务之前,这个重置的zxid(或者说是epoch)尚不能作为真正的zxid,即还不能持久在db中。此zxid经过大多数的Follower赞同之后(赞同:即所有现存的Follower都没有比此epoch更大的)才可以使用,在新leader提议这个epoch时,如果发现>=当前epoch时,leader就将较大的epoch +1并赋值给当前epoch,以确保新leader在服务器之前,所采用的epoch为最大的。(参见:Leader.getEpochToPropose())Epoch提议只会被计算一次,并且多数派时(包括leader在内),此epoch才能被生效,并参与接下来新的zxid生成。
  3. Leader.getEpochToPropose(sid,lastAcceptedEpoch),这个方法会在leader.lead()和LearnerHandler中调用,此方法会阻塞,直到leader提交自己的epoch,并且“大多数”Follower也提交(LearnerHandler中)。所以在上述2个方法中,均会像”栅栏”一样,阻塞每个peer的执行此方法比较leader.epoch和每个sid所交付的lastAcceptedEpoch,并确保leader.epoch是最大的(且不相等,相等时,会+1):
    if (lastAcceptedEpoch >= epoch) {
       epoch = lastAcceptedEpoch+1;
    }
    
     Leader.lead()即表示leader提交自己的epoch.;LearnerHandler线程时负责接受follower连接的,所以在每个follower建立连接时也会向leader首先交付自己的epoch.只有在epoch达成一致之后,才能进行实际的通讯.
  4. 经过3)之后,leader和follower确认了epoch(选举票根)之后,leader计算并db持久化zxid,同是将自己(leader)目前的(epoch,zxid)信息封装,在此同时各个参与选举的follower也做同样的操作,不过每个follower所交付的zxid可能不同,但是他们不能比当前leader更”超前”(如果超前,将抛出异常,系统认为leader职能失败,重新选举),各个follower陆续向leader确认(epoch,zxid),如果此时大部分参与选举的follower都参与了确认且正常,那么到此为止,leader和follower各自的角色被真正决定,同时follower和leader之间的zxid步差也将被计算出来.[follower的ack操作也是在LearnerHandler中进行,3]操作之后]
  5. 此期间有个小小的细节,leader在和follower确认epoch和zxid时,会创建一个”标记”性proposal,此提议的zxid为根据epoch新计算的其中counter(计数器位)为0,在learnerHandler.run中,所有的follower都同步完毕,并手动去提交ACK消息时(leader.processAck),当所有的follower都ACK之后,将会导致LeaderZookeeperServer启动,接下来leader就可以为client提供服务了.
  6. 其实代码的复杂度还是很大.在LearnerHandler.run()和Follower.followLeader()方法中存在顺序性的交互操作.那么Leader.lead()只是根据上述2个方法的交互作为状态判断(有同步的过程).
  7. 当leader和follower确认”角色”关系之后,每个follower所对应的LearnerHandler就可以从follower发送的packet中得到当前follower所持有的最大的zxid,通过此zxid和leader所持有commitedLog中最大的czxid比较,如果follower的zxid比较大,那么handler将会首先发送一个TRUNC信号,让follower清除czxid之后的所有记录;如果follower的zxid比较小,那么此时handler将会遍历commitedLog,将zxid之后的所有记录,封装成”提议”直接发送给follower(此提议为commit状态,即follower直接提交,不再ACK),当数据同步完毕之后,handler最后向follower交付一个UPTODATE的标记提议,告知follower,数据已经同步结束,它即可开始为client服务了.
  8. LearnerHandler向follower同步完数据之后,即进入while自选循环中,用于顺序接收follower所发送的各种类型的提议,并做不同的业务处理.那么同时Follower也进入了服务状态(while循环),接收client的请求,响应事件,转发write操作等.

你可能感兴趣的:(zookeeper)