• zookeeper技术内幕,简要记录;


    服务端:

    1,启动核心流程【以分布式为例】

    启动类:QuorumPeerMain

    首先初始化数据,因为整个流程十分复杂,所以只记录核心的主流程;

    首先,zookeeper会为每台机器创建一个QuorumPeer实例,代表着一台服务器;接着创建FileTxnSnapLog,用来处理事务日志的持久化(包括append追加和快照持久化),然后创建内存数据容器ZKDatabase,接着调用QuorumPeer实例的start方法启动;

    启动流程:

    1)根据初始化时创建的FileTxnSnapLog来将磁盘中的数据转储到内存ZKDatabase中;

    2)绑定本机端口

    3)开始选举

    4)独立线程启动

     

    启动过程子流程详解:

    1)根据初始化时创建的FileTxnSnapLog来将磁盘中的数据转储到内存ZKDatabase中

    因为zookeeper的文件名包含了zxid,所以这一步可以拿到磁盘文件中最大的zxid,放到内存中;

    找磁盘文件时,每次只会找100个文件;

    2)绑定本机端口

    有NIO和Netty两种方式,逻辑比较简单

    3)开始选举

    启动过程的选举,首先会确定一张选票,格式(long id, long zxid, long peerEpoch)

    然后创建一个网络连接器QuorumCnxManager用来跟其他服务器进行通信

    QuorumCnxManager的核心:

    一个消息接收队列BlockingQueue<Message> recvQueue

    消息发送队列集合queueSendMap<Long, BlockingQueue<ByteBuffer>>

    连接监听器Listener;

    如果创建的连接监听器有有效(不为空),则开始以下流程:

    监听器线程start,然后在会初始化连接处理器ListenerHandler线程;

    连接处理器ListenerHandler线程start,然后ListenerHandler会创建socket连接,并且不断监听来自其他服务器的网络连接socket请求,并且只会跟sid比自己大的服务器连接;

    当连接建立后,SendWorker线程和RecvWorker线程开始工作,start;

    SendWorker主要是负责从queueSendMap取出消息,发送给其他服务器;

    RecvWorker主要负责接收其他服务器的消息,保存到recvQueue中;

     

    做完以上步骤后,进入核心选举算法FastLeaderElection的流程中:

    FastLeaderElection 维护了

    一个消息发送队列LinkedBlockingQueue<ToSend> sendqueue,

    一个消息接收队列LinkedBlockingQueue<Notification> recvqueue

    消息发送线程WorkerSender

    消息接收线程WorkerReceiver

    紧接着start 

    WorkerSender线程和WorkerReceiver线程;

    WorkerSender会不断轮询从sendqueue取出消息进行发送;

     

    WorkerReceiver的处理逻辑:

    从recvQueue中取出消息

    1)如果是来非投票服务器(例如Observer)的消息,直接将自身选票放入sendqueue中,响应回去

    2)如果是来自投票服务器的消息,首先看消息中的状态是不是LOOKING,如果是LOOKING,并且消息中逻辑时钟小于当前

    服务器的逻辑时钟(逻辑时钟:logicalclock,也就是epoch—选举周期,用于标识当前选举轮次,每次选举轮次都会对该值自增,并且所有有效的投票必须在同一轮次中),则将自身选票放入sendqueue中,响应回去;否则看,如果消息中的状态是LOOKING,并且当前服务器不是LOOKING,也将

    自身选票放入sendqueue中,响应回去;

    以上主要为WorkerSender和WorkerReceiver的重要工作流程;

     

    Zookeeper中主要有两种情况需要进行选举;

    1,服务器启动(QuorumPeer线程调用start方法)

    2,服务器运行期间无法与leader保持连接

    针对第一种情况,当QuorumPeer线程调用start方法后,会进入到服务器启动的选举流程,会走到FastLeaderElection最为核心的lookForLeader

    方法中:

    最开投票的时候,每台服务器都会将自己推举为leader进行投票;

    1)如果当前服务器状态为LOOKING:首先自增逻辑时钟logicalclock,然后初始化自身选票,并且发送出去;

    紧接着接收外部投票(从recvQueue中获取),如果获取不到,会检查自己与其他服务器的连接,如果连接成功,会继续将初始化的选票发送出去;

    然后对比选举轮次:

     

    如果外部投票的选举轮次大于当前选举轮次logicalclock,那么立即更新当前选举轮次,并且清空所有已经收到的选票,

    接着用本地内部选票与外部投票进行PK,new代表外部选票,cur代表本地选票,

    (((newEpoch > curEpoch)|| ((newEpoch == curEpoch)&& ((newZxid > curZxid)|| ((newZxid == curZxid)&& (newId > curId)))))),PK完成后,更新

    本地选票,并且将其发送出去;

     

    如果外部投票的选举轮次小于当前选举轮次logicalclock,不做任何操作;

     

    如果外部投票的选举轮次等于当前选举轮次logicalclock,那么直接PK,PK完成后,更新本地选票,并且将其发送出去;

     

    上面的操作完成之后,进行选票归档,将收到的所有外部选票放入Map<Long, Vote> recvset中,其中key为外部服务器id

     

    然后统计投票:确认集群中是否有过半服务器(可以看看是怎么确认过半的。)接收了当前的内部选票,如果有,则终止投票,否则继续接收外部选票;

    统计完成之后,服务器开始更新服务器状态,看自己是否为leader,follwer或者observer;

    服务端通过processor调用链处理各种请求,包括客户端的请求;

     

    客户端:

    客户端与服务端建立连接后,监听服务端的socket请求,然后ClientCnxn中的EventThread读取请求数据,并且将请求放到

    EventThread中的LinkedBlockingQueue<Object> waitingEvents 事件等待队列中,开始不断轮询其中的请求事件,包括watcher监听事件;

    还有Session会话等内容,后续有时间补充。

  • 相关阅读:
    一些技术鸡汤
    css优化
    Spring 通过maven pom文件配置初始化
    sql 编写横竖表转换
    Linux 常用命令笔记 (持续更新)
    java常用集合详解 contains
    jQuery Ajax(异步请求)
    java中"与"和"或"
    java8 中的时间和数据的变化
    mysql 根据查询结果集更新
  • 原文地址:https://www.cnblogs.com/mmh760/p/15431630.html
Copyright © 2020-2023  润新知