ZooKeeper源码阅读(五)---服务端集群流程

从本文开始正式开始学习zk中核心集群的实现原理。 

1、单机调试集群方法

首先创建三个zoodata目录,并将默认zoo.cfg文件拷贝进去

 单独配置每个cfg文件:

dataDir[持久化文件与myid文件保存的地址,每个配置文件均不同],clientPort[客户端接口端口,单机测试保证均不相同],server[集群服务地址,配置相同],最后在各自的dataDir目录下创建myid文件[内容为0,1,2编号,与cfg里server.编号 相同]。之后就可以idea配置不同的启动参数,进行集群的调试了。

dataDir=/zookeeper/apache-zookeeper-3.7.0/conf/zoocfg/zoo3
clientPort=2183
server.0=127.0.0.1:2888:3888
server.1=127.0.0.1:2889:3889
server.2=127.0.0.1:2890:3890

2、启动流程

首先我们开启三个server,然后关闭一个,打上断点重新开启服务,逐步调试

//首先区别于单机,集群的入口变为了
runFromConfig(){

    quorumPeer.initialize();

    quorumPeer.start();

}

quorumPeer.start(){
    //加载磁盘数据
    loadDataBase();
    //开启io服务
    startServerCnxnFactory();
    //进行选举
    startLeaderElection();
    //peer主循环
    super.start();
}

startLeaderElection(){
    this.electionAlg = createElectionAlgorithm(electionType);
}

createElectionAlgorithm(){
    //这个版本只剩case 3 的选举方法了
    //服务器之间用于选举的连接管理类
    createCnxnManager();
    QuorumCnxManager.Listener listener = qcm.listener;
    //与其他服务建立连接与监听
    listener.start();
    FastLeaderElection fle = new FastLeaderElection(this, qcm);
    //开启选举算法消息收发线程
    fle.start();

}

3、选举算法

首先选举算法的实现,3.7这个版本用于选举的算法只剩FastLeaderElection这个类了,这里深入学习一下,算法参数我们就先不深究了,先来看看整体的架构,可以看到整体的逻辑是FastLeaderElection类维护了与其他peer的连接,并处理收发消息(均是通过队列+线程的异步形式),发送消息就是简单的从队列中取出消息然后发送,而接收消息实现逻辑较为复杂,主要是针对收发方不同的节点状态以及选票信息进行选票的轮换,这里我们简化一下:

public class FastLeaderElection implements Election {
    //与其他服务端的TCP连接管理,处理实际io的收发
   QuorumCnxManager manager;
    //发送选票消息队列---tosend可以是Notification或者ack
    LinkedBlockingQueue<ToSend> sendqueue;
    //接收选票消息队列---Notification为选票改变消息
    LinkedBlockingQueue<Notification> recvqueue;
    //负责实际收发消息的处理类
    Messenger messenger;
}
//内部类,处理算法层面的收发选票
protected class Messenger {
    
   
   WorkerSender{
        
        run(){
            //发送消息,添加进发送队列
            recvqueue.add()
        }        

    }

    WorkerReceiver{
        
        run(){

            response = manager.pollRecvQueue(3000, TimeUnit.MILLISECONDS);
            1、参数校验
            2、都不为Looking则将当前的选举结果返回给发送方
            3、若都为Looking根据接收到的选举轮次与最大事务id,分别执行不同的算法流程
                +大体规则为:忽略选举轮次小的消息,给选举大的消息投票,若相同则比较zxid,sid大的优先
        }
    }
}

那么选举是如何开始的呢,从上文在服务器启动过程中,会开启选举相关连接与线程。在zk中会在两种情况下开始选举:1、节点启动  2、follower与leader丢失连接。这里我们先看一下在2小节略过的peer主loop,在LOOKING状态会调用lookForLeader进行选举。

while (running) {

case LOOKING:
    //开启选举
    startLeaderElection();
    setCurrentVote(makeLEStrategy().lookForLeader());
case OBSERVING:
    setObserver(makeObserver(logFactory));
    observer.observeLeader();
case FOLLOWING:
    setFollower(makeFollower(logFactory));
    follower.followLeader();
case LEADING:
    setLeader(makeLeader(logFactory));
    leader.lead();

}

FastLeaderElection的主要选举逻辑在lookForLeader方法里,这里就直接偷一张流程图了,代码整体逻辑比较复杂:

4、peer主loop

从上节可以看到peer会根据节点状态进入不同的处理逻辑,并一直循环下去。新的服务启动之后LOOKING状态,会发起一次选举流程。待选举完成之后,则各自进入following与leading流程,这里从leader视角分析以下主要流程:

leader流程

1、启用新的epoch,并与每个follower建立连接,连接处理采用LearnerHandler线程

2、发送NEWLEADER包,并等待follower完成同步

3、周期发送ping给follower维持连接

4、LearnerHandler线程处理具体业务逻辑交互主要是是否进行数据同步与ACK交互

5、对外client请求的处理也采用责任链模式,为以下这些

PrepRequestProcessor:创建和修改状态的Request关联的header和txn
ProposalRequestProcessor:将写请求发送proposal到所有的follower
SyncRequestProcessor:将发出去的proposal批量写入磁盘
AckRequestProcessor:当proposal真正写入了磁盘后,向本机发送ack包
CommitProcessor:匹配本地submitted的请求和收到的committed的请求
ToBeAppliedRequestProcessor:把写入到磁盘的proposal从toBeApplied中删除
finalProcessor:把commit的proposal写入到本机的内存状态中
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值