Zookeeper的启动流程

Zookeeper的主类是QuorumPeerMain,启动时读取zoo.cfg配置文件,如果没有配置server列表,则单机模式启动,否则按集群模式启动,这里只分析集群模式。

根据配置初始化quorumPeer对象,并启动quorumPeer线程,这里主要做了几件事情:

  1.   读取保存在磁盘上的数据,包括db的snapshot和txnlog,zookeeper的存储结构另外专门讲述。

  2.   启动cnxnFactory,这里主要是启动一个server,用来接收来自client的请求,绑定在配置文件中的clientPort端口。

  3.   在QuorumAddress上绑定一个server,用来和其他zookeeper server做交互。

  4.   启动leader选举过程,因为server刚启动时是存在LOOKING状态,需要发一起一次选举过程来获取leader。

  5.   启动quorumPeer的主线程run,根据当前节点的状态来启动不同的流程。

如果是Looking状态,则调用FastLeaderElection::lookForLeader来发起选举流程

如果是OBSERVING状态,则开始Observer流程

如果是FOLLOWING状态,则开始Follower流程

如果是LEADING状态,则开始Leader流程

Zookeeper的选举流程

发起选举流程有两种情况:

1)server刚启动的时候,server的状态初始化为LOOKING状态

2)server发生异常,切换到LOOKING状态

 server有4中状态:

LOOKING:初始状态,表示在选举leader

FOLLOWING:跟随leader的角色,参与投票

LEADING:集群的leader

OBSERVING:不参与投票,只是同步状态

  按刚启动来讲述选举流程,QuorumPeer::start() -> QuorumPeer::startLeaderElection() -> QuorumPeer::createElectionAlgorithm,默认使用FastLeaderElection算法,初始化的流程如下:

  初始化QuorumCnxManager,管理选举中和其他server的交互,选举时监听在专门的electionAddr上。

  QuorumCnxManager是实际发生网络交互的地方,它的主要数据结构包括:

  • queueSendMap:sid -> buffer queue,为每个参与投票的server都保留一个队列
  • recvQueue:message queue,所有收到的消息都放到recvQueue
  • listener:server主线程,收发消息时和上面两个队列交互

  QuorumCnxManager可以保证每对peer之间只有一个链接,如果有server发起新的链接,则比较sid,sid大的保留链接,小的放弃链接

初始化FastLeaderElection,这是选举逻辑所在的地方,它主要包括3个线程:

Messenger::WorkerReceiver:从QuorumCnxManager::recvQueue中获取网络包,并将其发到FastLeaderElection::recvqueue中。

Messenger::WorkerSender:从FastLeaderElection::sendqueue中获取网络包,并将其放到QuorumCnxManager::queueSendMap中,并发送到网络上lookForLeader:QuorumPeer。

主线程会调用lookForLeader函数,它从recvqueue中获取别人发给server的选举数据,并将发给其他server的投票放到sendqueue中。

FastLeaderElection::lookForLeader中实现了选举算法,具体的流程如下:

  首先更新选举周期logicalclock,并把自己作为leader作为投票发给所有其他的server。

  然后进入本轮投票的循环。

从recvqueue获取一个网络包,如果没有收到包则检查是否要重连和重发自己的投票。

  收到投票后判断投票的状态。

LOOKING:

       如果对方投票的周期大于自己的周期,那就清空自己的已经收到的投票集合recvset,并将自己作为候选和对方投票的leader做比较,选出大的作为新的投票,然后再发送给所有人。

       这里比较大小是通过比较(zxid,sid)这个二元组来的,zxid大的就大,否则sid大的就大。

  • 如果对方的投票周期小于自己,则忽略对方的投票
  • 如果周期相等,则比较对方的投票和自己认为的候选,选出大的作为新的候选,然后再发送给所有人
  • 然后判断当前收到的投票是否可以得出谁是leader的结论,这里主要是通过判断当前的候选leader在收到的投票中是否占了多数
  • 如果候选leader在收到的投票中占了多数,则再等待finalizeWait时钟,看是否有人修改leader的候选,如果修改了则把投票放到recvqueue中再从新循环

OBSERVING:

  如果对方是一个观察者,由于它没有投票权,则无视它

FOLLOWING或LEADING:

  • 如果对方和自己再一个时钟周期,说明对方已经完成选举,如果对方说它是leader,那我们就把它作为leader,否则就要比较下对方选举的leader在自己这里是否占有多数,并且选举的leader确认了愿意当leader,如果都通过了,就把这个候选作为自己的leader
  • 如果对方和自己不再一个时钟周期,说明自己挂掉后又恢复起来,这个时候把别人的投票收集到一个单独的集合outofelection(从名字可以看出这个集合不是用在选举判断),如果对方的投票在outofelection中占有大多数,并且leader也确认了自己愿意做leader,这个时候更新自己的选举周期logicalclock,并修改自己的状态为FOLLOWING或LEADING

Leader执行流程

QuromPeer线程

Leader选举完成之后,Peer确认了自己是Leader的身份,在QuromPeer的主线程中执行Leader的逻辑创建Leader对象,并创建Server绑定在QuorumAddress上,用于和其他Follower之间相互通信调用Leader::lead函数,执行Leader的真正的逻辑。

  调用ZooKeeperServer::loadData,从磁盘中恢复数据和session列表

  启用新的epoch,zookeeper中的zxid是64位,用于唯一标示一个操作,zxid的高32位是epoch,每次Leader切换加1,低32位为序列号,每次操作加1

  启动绑定在QuorumAddress上的Server,为每个Follower的连接建立一个LearnerHandler,用于和Follower做交互,这里的逻辑另外单独论述

  向所有的Follower发送一个NEWLEADER包,宣告自己额Leader身份,并在initLimit时间内等待大多数的Follower完成和Leader的同步,并发送ACK包,表示Follower已经和Leader完成同步并可以对外提供服务.

  这时Leader和Client之间的交互在cnxnFactory的Server中,Leader和Follower之间的交互在LearnerHandler所属的线程中。 

  然后调用Leader::lead函数的QuromPeer线程在每个tickTime中都会发送2个ping消息给其他的follower,follower在接收到ping消息后会回复一个ping消息,并附带上follower的session tracker里的所有session信息,leader收到follower的ping消息后,根据传回的session信息更新自己的session信息

LearnerHandler线程

        LearnerHandler主要是处理Leader和Follower之间的交互,和每个Follower连接会维持一个长连接,并有一个单独的LearnerHandler线程和一个Follower进行交互当Follower和Leader建立连接后,会先发一个FOLLOWERINFO包,包含了follower的server id和最近的一个zxid,即peerLastZxid,根据peerLastZxid来判断如何与Follower进行同步。

如果peerLastZxid大于leader的最新的zxid,则给follower发送trunc包,让follower删掉多出来的事务,一般来说这种情况比较少

如果peerLastZxid小于leader的最新的zxid,则给follower发送diff包,让follower补齐和leader之间的差距

同步时发送包的顺序如下:

NEWLEADER(同步发送)

DIFF(同步发送)

以下包的发送在一个线程中异步发送

循环发送写入磁盘的txn和commit包

循环发送已经commit但还未写入磁盘的toBeApplied数组的txn和commit包

循环发送已经提出proposal但还未commit的outstandingProposals数组中的txn,注意这里没有发送commit包

       为了和follower做快速的同步,leader会在内存中缓存一部分最近的事务,即minCommittedLog和maxCommittedLog之间的事务,如果peerLastZxid比minCommittedLog还小的话,leader就给follower发送一个snap包,把当前leader的镜像发给follower。

       同步等待第一个回复的ACK包,然后计算同步超时tickTime*syncLimit,同步的后续的ACK包在下面的循环中处理,循环处理和follower之间交互的包

ACK包:调用leader.processAck方法,processAck函数的执行逻辑如下:

  1. 如果Ack包的zxid小于Leader的lastCommitted,则忽略
  2. 根据ack包的zxid,在outstandingProposals中找出对应的proposal
  3. 将ack包对应的follower的sid加入proposal的ackset,如果ackset中超过大多数,则表示这个proposal可以commit
  4. 从outstandingProposals中删除这个proposal,并把这个proposal加入到已经可以commit的toBeApplied数组中
  5. 向follower发送commit包,通知follower将proposal提交
  6. PING包:用于和follower同步session信息
  7. REQUEST包:follower转发过来的修改状态的请求,调用ZooKeeperServer::submitRequest方法,这个方法后面单独论述

NIOServerCnxn::Factory线程

  • 该线程主要负责server和client的交互
  • 该server是基于select的,当有客户端连接server时,会调用doIO逻辑,把socket上的数据读取出来解析并处理(readPayload函数),并把需要写出的outgoingBuffers写入socket
  • 如果是刚连接上,则调用readConnectRequest,这里会调用submitRequest(cnxn,sessionId, OpCode.createSession, 0, to, null);实际是发起一个创建session的请求
  • 如果不是第一次连接,则调用readRequest函数,这里会从socket上读出Request数据,然后调用submitRequest
  • 我们可以看到来自client的请求和来自其他server的请求都会调用submitRequest函数,这个函数会调用server上的RequestProcessor链,server实现的是责任链模式,每个请求都会经过责任链里所有RequestProcessor的处理

          对于Leader来说,LeaderZooKeeperServer::setupRequestProcessors设置了Leader用到的责任链,按从前到后的顺序如下:

PrepRequestProcessor:创建和修改状态的Request关联的header和txn

ProposalRequestProcessor:将写请求发送proposal到所有的follower

SyncRequestProcessor:将发出去的proposal批量写入磁盘

AckRequestProcessor:当proposal真正写入了磁盘后,向本机发送ack包

CommitProcessor:匹配本地submitted的请求和收到的committed的请求

ToBeAppliedRequestProcessor:把写入到磁盘的proposal从toBeApplied中删除

finalProcessor:把commit的proposal写入到本机的内存状态中

Zookeeper-技术专区-运作流程分析介绍的更多相关文章

  1. Android bluetooth介绍(四): a2dp connect流程分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  2. nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)

    本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...

  3. Cocos2d-x3.3RC0的Android编译Activity启动流程分析

    本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...

  4. 第2章 rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是本文的姊妹篇: 1.rsync(一):基本命令和用法 2.rsync(二):ino ...

  5. Netty 拆包粘包和服务启动流程分析

    Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你能掌握EventLoopGroup的工作流程,ServerBootstrap的启动流程,ChannelPipeline是如何操作管理Ch ...

  6. rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释.本文不会介绍如何使用rsync命令(见rsync基本用法),而是详细解释它如何实现高效的增 ...

  7. Uboot启动流程分析(转载)

    最近一段时间一直在做uboot移植相关的工作,需要将uboot-2016-7移植到单位设计的ARMv7的处理器上.正好元旦放假三天闲来无事,有段完整的时间来整理下最近的工作成果.之前在学习uboot时 ...

  8. 【转】Netty 拆包粘包和服务启动流程分析

    原文:https://www.cnblogs.com/itdragon/archive/2018/01/29/8365694.html Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你 ...

  9. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

随机推荐

  1. .Net Core应用RabbitMQ,及简单封装

    首先,还是万分感谢大家能够抽空来阅读我的文章,万分感谢.今天我带来的是.Net Core中应用RabbitMQ,和简单封装.因为昨天的文章里说了今天要写,所以今天一定要写出来.小编翻阅了很多资料,想要 ...

  2. 六、hibernate表与表之间的关系(多对多关系)

    多对多关系 创建实体类和对应映射文件 Student.java package com.qf.entity; import java.util.HashSet; import java.util.Se ...

  3. 基于QRcode的带有文字+图片的二维码的Vue组件

    1 <template> 2 <!-- 生成二维码开放接口: 3 二维码内容[通常为url] 4 二维码大小[限制为正方形] 二维码下方显示:文字 5 二维码中间显示:图片--> ...

  4. js中的script标签属性

    HTML <script> 元素用于嵌入或引用可执行脚本. 在html中插入一个script标签 <script src="index.js" sync cros ...

  5. OkHttp的使用

    Download OkHttp3 implementation 'com.squareup.okhttp3:okhttp:3.10.0' 1.1. 异步GET请求 -new OkHttpClient; ...

  6. shell使用reposync同步仓库

  7. 关于Django路由层简单笔记

    Django—路由层 URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来的某个U ...

  8. 转:动态库路径配置- /etc/ld.so.conf文件

    Linux 共享库 Linux 系统上有两类根本不同的 Linux 可执行程序.第一类是静态链接的可执行程序.静态可执行程序包含执行所需的所有函数 — 换句话说,它们是“完整的”.因为这一原因,静态可 ...

  9. Python3.5-20190506-廖老师-自我笔记函数

    函数就是将你的代码封装起来,可以重复利用.不需要每次就写重复的代码 def 函数名(位置参数,默认参数=10,可变参数,关键字参数): 代码块 return 值 定义函数时,需要确定函数名和参数个数: ...

  10. WebDriverAgent安装

    这次安装WebDriverAgent的过程可谓坎坷呀,最后还是大牛远程解决问题,自己的确差太远,记录一下过程吧 尽量升级Xcode到最新版,保持iPhone的版本大于9.3 终端进入目标文件夹WebD ...