zookeeper源码_zookeeper源码解读之-Leader选举

注意:如果想要了解更多大数据和算法相关资料,关注有惊喜哦

4ab917cca290e53068cf7e9724a4653c.png

上文已经对zookeeper服务端的整体启动流程进行了解读,那么接下来将针对QuorumPeer.start方法内部的具体流程做更一步的细化解读

一、服务启动之-加载快照、事务文件到内存生成DataTree模型

  1. 应用快照文件QuorumPeer在实例化的时候,会创建ZKDatabase实例,该类有以下几个属性;然后会调用该类的loadDataBase方法
42ed9f085347686a0d00e0f7f7311b5a.png
5833c300e193e226a12aa9609c474f41.png

2.获取最近一次提交的事务id

3.从zxid中解析获取epoch任期,以及从文件中解析当前epoch

二、服务启动之- Netty服务启动

服务器从快照中恢复数据构建出DataTree模型后,开始启动serverCnxn,建立socket通道,这里启动主要对应两个实现:NIO服务和Netty服务,具体选择哪一种服务,是在初始化quorumPeer时进行配置的

  1. 创建ServerCnxnFactory调用ServerCnxnFactory.createFactory()方法实例化一个cnxnFactory,而具体选择哪种服务,已经被封装起来,具体细节可见createFactory方法

1.1 可以看到通过反射的方式生成ServerCnxnFactory,默认使用的是NIO,可以通过zookeeper.serverCnxnFactory参数进行配置,如果选用Netty,则配置成 NettyServerCnxnFactory.class.getName()

1.2 这里通过NettyServerCnxnFactory来看一下内部细节

1.2.1 构造函数中,初始化ServerBootstrap对象,设置TCP参数,设置channelHandler

1.2.2 CnxnChannelHandler继承自ChannelDuplexHandler, 它被标注为@Sharable,还是一个共享的处理器。主要 处理各种IO事件。比如客户端连接、断开连接、可读消息

df38103607d284044d1bcfbc626e9cb3.png

2.调用ServerCnxnFactory.start方法来启动Netty服务

通过调用quorumPeer.start方法来调用cnxnFactory.start方法来启动Netty服务。

quorumPeer.start方法内部封装了一个startServerCnxnFactory方法,而startServerCnxnFactory方法底层调用了cnxnFactory.start方法,即quorumPeer.start--->startServerCnxnFactory--->cnxnFactory.start

77e623e7e96caea8d6d83317fadbf3aa.png
dd9074effe7f8f9f227852482988e725.png

三、服务启动之-Leader选举

1.选举初始化

Leader选举入口: QuorumPeer.startLeaderElection() ,大致流程如下:

  1. 判断当前服务器状态是否为LOOKING,如果是则创建Vote投票实例,服务刚启动时会把票投给自己
  2. 创建QuorumCnxManager实例,主要负责集群中各个节点的网络IO
  3. QuorumCnxManager有一个内部类Listener,会启动一个线程用于监听选举端口并处理连接进来的Socket
  4. 构建一种选举算法FastLeaderElection,早期Zookeeper实现了四种选举算法,但是后面废弃了三种,最新版本只保留FastLeaderElection这一种选举算法
faff6615d1f376e062f13107c2574ba3.png

2.网络io

fb35fde624d289ff46c6eb8f7b0b6040.png

Leader选举涉及到两个核心类:QuorumCnxManager和FastLeaderElection

红线之上是 QuorumCnxManager工作区域

红色线之下的是FastLeaderElection工作区域 。

选举算法逻辑被封装在FastLeaderElection类中 ;

而QuorumCnxManager则用于管理维护选举期间的网络IO.大致流程:

1.初始化阶段,会实例化QuorumCnxManager,而且会创建内部listener线程,创建ServerSocket,然后循环等待客户端连接(注意:这里的客户端指的是集群其他节点)

2.当有客户端连接进来后,会把该客户端socket封装成 RecvWorker和SendWorker,它们都是线程,分别负责和该Socket所代表的客户端进行读写;RecvWorker和SendWorker是成对出现的,每对负责维护和集群中的一台服务器进行网络IO通信 (注意:这里为了避免资源浪费,只需建立一条通道,即当接收到到一个请求时会进行比对myid,如果对端myid大于自己,则认为连接有效,也就是myid小的一方作为服务端)

3.FastLeaderElection负责Leader选举核心规则算法实现,注意FastLeaderElection类中也包含了两个内部类WorkerSender和WorkerReceiver,类似于QuorumCnxManager中的SendWorker和RecvWorker,也是用于发送和接收线程

4.FastLeaderElection中进行选举时广播投票信息时,将投票信息写入到对端服务器大致流程如下:

4.1.将数据封装成ToSend格式放入到sendqueue;

4.2.WorkerSender线程会一直轮询提取sendqueue中的数据,当提取到ToSend数据后,会获取到集群中所有参与Leader选举节点(除Observer节点外的节点)的sid,如果sid即为本机节点,则转成Notification直接放入到recvqueue中,因为本机不再需要走网络IO;否则放入到queueSendMap中,key是要发送给哪个服务器节点的sid,ByteBuffer即为ToSend的内容,queueSendMap维护的着当前节点要发送的网络数据信息,由于发送到同一个sid服务器可能存在多条数据,所以queueSendMap的value是一个queue类型;

4.3.QuorumCnxManager中的SendWorkder线程不停轮询queueSendMap中是否存在自己要发送的数据,每个SendWorkder线程都会绑定一个sid用于标记该SendWorkder线程和哪个对端服务器进行通信,因此,queueSendMap.get(sid)即可获取该线程要发送数据的queue,然后通过queue.poll()即可提取该线程要发送的数据内容;

4.4.然后通过调用SendWorkder内部维护的socket输出流即可将数据写入到对端服务器

5.FastLeaderElection中进行选举时广播投票信息时,从对端服务器读取投票信息的大致流程如下:

5.1.QuorumCnxManager中的RecvWorker线程会一直从Socket的输入流中读取数据,当读取到对端发送过来的数据时,转成Message格式并放入到recvQueue中;

5.2.FastLeaderElection.WorkerReceiver线程会轮询方式从recvQueue提取数据并转成Notification格式放入到recvqueue中;

5.3.FastLeaderElection从recvqueue提取所有的投票信息进行比较 最终选出一个Leader

951747d068b0dc585eaf6bd5c621261f.png
77bea1e5cb47eeb26c815fdb0994885d.png
261e902e0119625560fcf41b00fa5cfb.png

3.Leader选举规则

Leader选举策略使用的是FastLeaderElection ,当检测到serverState状态为LOOKING时进入到LOOKING分支中调用lookForLeader方法开始选举,具体流程如下:

3.1.调用 FastLeaderElection.lookForLeader方法 ,开始选举

3.2.更新自己期望投票信息,即自己期望选哪个服务器作为Leader(用sid代替期望服务器节点)以及该服务器zxid、epoch等信息,第一次投票默认都是投自己当选Leader,然后调用sendNotifications方法广播该投票到集群中所有可以参与投票服务器 (注意:调用 updateProposal 方法来更新投票信息,有三个参数: a.期望投票给哪个服务器(sid)、b.该服务器的zxid、c.该服务器的epoch )

3.3.然后等待其他服务器发送给自己投票信息

3.4.将接收到的投票state进行判断确定接下来执行哪个逻辑

3.4.1 当接收到的状态为LOOKING时,接下来首先比较epoch,如果接收到的epoch比自己的要大,此时要清空之前获取的所有投票, 因为之前获取的投票轮次落后于当前则代表之前的投票已经无效了 ,然后开始根据epoch,zxid,sid进行PK,最后将pk结果同步出去;

当接收到的epoch小于自己时,则直接省略;

当接收到的epoch和自己的一致时,则将投票放入到投票列表集合中,然后进行PK,看是否能够选举出Leader,如果能选举出,则选举结束,并更改自身状态,否则需要继续接收投票;

3.4.2 当接收到的状态为OBSERING时,则忽略投票信息;因为OBSERING不参与投票

3.4.3 当接收到的状态为LEADING时,这时只需要验证是否有效,如果有效则选举结束,否则继续接收投票信息

c045bb0f5cb686f706ed33349e1be60f.png
4f7e2e9518b00a90999c12b24d35419c.png
4182bbae27e42e83a006dd1026751324.png

以上就是对QuorumPeer.start方法入口的主要流程进行了解读,那么当Leader选举出来之后,此时还不能对外提供服务,还需要进行最后一步,即数据同步,保证集群数据一致的情况下,这时才能对外提供服务。那么数据同步就要涉及到Leader端、Follower端和Observer端之间的处理逻辑,该部分内容将在下一篇中详解解读。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值