首次购买高性能云服务器,享低至2折优惠

搜搜吧

查看: 7|回复: 0

[资讯] zk集群运行过程中,服务器选举的源码剖析[含5P]

[复制链接]
  • TA的每日心情
    擦汗
    昨天 15:56
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    超级VIP贵宾会员

    83

    主题

    86

    帖子

    273

    积分

    Rank: 1

    UID
    21335
    威望
    -3
    贡献
    108
    在线时间
    1 小时
    注册时间
    2018-12-6
    发表于 6 天前 | 显示全部楼层 |阅读模式
    推广者专属福利,新客户无门槛领取总价值高达2775元代金券,每种代金券限量500张,先到先得。

    在zk服务器集群启动过程中,经QuorumPeerMain中,不光会创建ZooKeeperServer对象,同时会生成QuorumPeer对象,代表了ZooKeeper集群中的一台机器。在整个机器运行期间,负责维护该机器的运行状态,同时会根据情况发起Leader选举。下图是 《从PAXOS到ZOOKEEPER分布式一致性原理与实践》的服务器启动流程。

    QuorumPeer是一个独立的线程,维护着zk机器的状态。

    • @Overridepublic synchronized void start() {
    •     loadDataBase();
    •     cnxnFactory.start();         
    •     startLeaderElection();    super.start();
    • }

    本次主要介绍的是选举相关的内容,至于其他操作可以看其他博客。之后的行文都是从startLeaderElection中衍生出来的。

    e24ca970a2d2e8a41710c51043bb9b2c.jpg-wh_651x-s_1577258204.jpg

    eacc2bbb05104cbe7ea6fb5fc728787a.jpg

    基本概念:

    SID:服务器ID,用来标示ZooKeeper集群中的机器,每台机器不能重复,和myid的值一直

    ZXID:事务IDVote: 选票,具体的数据结构后面有

    Quorum:过半机器数

    选举轮次:logicalclock,zk服务器Leader选举的轮次

    服务器类型:

    在zk中,引入了Leader、Follwer和Observer三种角色。zk集群中的所有机器通过一个Leader选举过程来选定一台被称为Leader的机器,Leader服务器为客户端提供读和写服务。Follower和Observer都能够提供读服务,唯一的区别在于,Observer机器不参与Leader选举过程,也不参与写操作的过半写成功策略。因此,Observer存在的意义是:在不影响写性能的情况下提升集群的读性能。

    服务器状态:

    + LOOKING:Leader选举阶段+ FOLLOWING:Follower服务器和Leader保持同步状态+ LEADING:Leader服务器作为主进程领导状态。+ OBSERVING:观察者状态,表明当前服务器是Observer,不参与投票

    选举的目的就是选择出合适的Leader机器,由Leader机器决定事务性的Proposal处理过程,实现类两阶段提交协议(具体是ZAB协议)

    QuorumPeer维护集群机器状态

    QuorumPeer的职责就是不断地检测当前的zk机器的状态,执行对应的逻辑,简单来说,就是根据服务所处的不同状态执行不同的逻辑。删除了一部分逻辑后,代码如下:

    • @Overridepublic void run() {     
    • setName("QuorumPeer" + "[myid=" + getId() + "]" +
    •             cnxnFactory.getLocalAddress());      
    • try {         
    • while (running) {            
    • switch (getPeerState()) {            
    • case LOOKING:               
    • LOG.info("LOOKING");                 
    • try {                     
    •     setBCVote(null);                           setCurrentVote(makeLEStrategy().lookForLeader());
    •                 }  
    • catch (Exception e) {                     
    • LOG.warn("Unexpected exception", e);                   setPeerState(ServerState.LOOKING);
    •           }                 
    •                 break;            
    • case OBSERVING:                 
    • try {                     
    • LOG.info("OBSERVING");                    setObserver(makeObserver(logFactory));                    observer.observeLeader();
    •                 } catch (Exception e) {                    LOG.warn("Unexpected exception",e );                        
    •                 } finally {                     
    • observer.shutdown();                     
    • setObserver(null);                     
    • setPeerState(ServerState.LOOKING);
    •                 }                 
    • break;            
    • case FOLLOWING:                 
    • try {                     
    • LOG.info("FOLLOWING");                    setFollower(makeFollower(logFactory));                    follower.followLeader();
    • } catch (Exception e) {                     
    • LOG.warn("Unexpected exception",e);
    •                 } finally {                     
    • follower.shutdown();                     
    • setFollower(null);                     
    • setPeerState(ServerState.LOOKING);
    •                 }                 
    • break;            
    • case LEADING:                 
    • LOG.info("LEADING");                 
    • try {                     
    • setLeader(makeLeader(logFactory));                     
    • leader.lead();                     
    • setLeader(null);
    •                 } catch (Exception e) {                    LOG.warn("Unexpected exception",e);
    •                 } finally {                     
    • if (leader != null) {                        
    • leader.shutdown("Forcing shutdown");                        setLeader(null);
    •                     }                     
    • setPeerState(ServerState.LOOKING);
    •                 }            
    • break;
    •             }
    •         }
    •     } finally {         
    • LOG.warn("QuorumPeer main thread exited");
    •     }
    • }

    当机器处于LOOKING状态时,QuorumPeer会进行选举,但是具体的逻辑并不是由QuorumPeer来负责的,整体的投票过程独立出来了,从逻辑执行的角度看,整个过程设计到两个主要的环节:

    • 与其他的zk集群通信的过程
    • 实现具体的选举算法

    而QuorumPeer中默认使用的选举算法是FastLeaderElection,之后的分析也是基于FastLeaderElection而言的。

    选举过程中的整体架构

    在集群启动的过程中,QuorumPeer会根据配置实现不同的选举策略this.electionAlg = createElectionAlgorithm(electionType);

    • protected Election createElectionAlgorithm(int electionAlgorithm){
    •     Election le=null;    switch (electionAlgorithm) {    case 3:
    •         QuorumCnxManager qcm = new QuorumCnxManager(this);
    •         QuorumCnxManager.Listener listener = qcm.listener;        if(listener != null){
    •             listener.start();
    •             le = new FastLeaderElection(this, qcm);
    •         } else {
    •             LOG.error("Null listener when initializing cnx manager");
    •         }        break;    default:        assert false;
    •     }    return le;
    • }

    如果ClientCnxn是zk客户端中处理IO请求的管理器,QuorumCnxManager是zk集群间负责选举过程中网络IO的管理器,在每台服务器启动的时候,都会启动一个QuorumCnxManager,用来维持各台服务器之间的网络通信。

    对于每一台zk机器,都需要建立一个TCP的端口监听,在QuorumCnxManager中交给Listener来处理,使用的是Socket的阻塞式IO(默认监听的端口是3888,是在config文件里面设置的)。在两两相互连接的过程中,为了避免两台机器之间重复地创建TCP连接,zk制定了连接的规则:只允许SID打的服务器主动和其他服务器建立连接。实现的方式也比较简单,在receiveConnection中,服务器会对比与自己建立连接的服务器的SID,判断是否接受请求,如果自己的SID更大,那么会断开连接,然后自己主动去和远程服务器建立连接。这段逻辑是由Listener来做的,且Listener独立线程,receivedConnection,建立连接后的示意图:

    c035845e8378dd8cf1a612c35678da22.jpg

    QuorumCnxManager是连接的管家,具体的TCP连接交给了Listener,但是对于选票的管理,内部还维护了一系列的队列:

    • recvQueue:消息接收队列,用来存放那些从其他服务器接收到的消息,单独的队列
    • 分组队列(quorumCnxManager中将zk集群中的每台机器按照SID单独分组形成队列集合):

    queueSendMap:消息发送队列,用于保存待发送的消息。new ConcurrentHashMap

    senderWorderMap:发送器集合。每个SendWorder消息发送器,都对应一台远程zk服务器,负责消息的发放。

    lastMessageSent:最近发送过的消息,按照SID分组

    基本的通信流程如下:

    3218038d1592228308ffe8ec9c3614a0.jpg

    以上内容主要是建立各台zk服务器之间的连接通信过程,具体的选举策略zk抽象成了,主要分析的是FastLeaderElection方式(选举算法的核心部分):

    • public interface Election {    public Vote lookForLeader() throws InterruptedException;    public void shutdown();
    • }

    FastLeaderElection选举算法

    上面说过QuorumPeer检测到当前服务器的状态是LOOKING的时候,就会进行新一轮的选举,通过setCurrentVote(makeLEStrategy().lookForLeader());也就是FastLeaderElection的lookForLeader来进行初始选择,实现的方式也很简单,主要的逻辑在FastLeaderElection.lookForLeader中实现:

    ea31323b95e46ded1da17c8c04e2f177.jpg

    基本流程先说明一下:

    • QuorumPeer会轮询检查当前服务器状态,如果发现State是LOOKING,调用Election的lookForLeader来开始新一轮的选举
    • FastLeaderElection会首先将logicallock++,表示新的一轮选举开始了
    • 构造初始的选票,Vote的内容就是选自己,然后通知zk集群中的其他机器
    • FastLeaderElection会一直轮询查状态,只要是LOOKING态,就会从recvqueue中获取其他服务器同步的选票信息,为了方便说明,记录为n
    • 根据n的票选信息状态,做相关的操作

    LOOKING: 都处于无Leader态,比较一下选票的优劣,看是否更新自己的选票,如果更新了就同时通知给其他服务器

    FOLLOWING、LEADING:说明集群中已经有Leader存在,更新一下自己的状态,结束本轮投票

    OBSERVING:这票没什么卵用,直接舍弃(OBSERVER是不参与投票的)

    根据上面的流程,可以大概说明一下FasterLeaderElection确定选票更优的策略:

    • 如果外部投票中被推举的Leader服务器选举轮次大于自身的轮次,那么就更新选票
    • 如果选举轮次一致,就对比两者的ZXID,ZAB协议中ZXID越大的留存的信息也越多,因此如果ZXID大于自己的,那么就更新选票
    • 如果ZXID也一致,对比两者的SID,SID大,则优先级高

    总结:

    以上就是zk的默认选票流程,按照ZAB协议的两种状态分析:

    • 初始化的时候,处于同一轮次进行投票直到投票选择出一个Leader
    • 崩溃恢复阶段:

    Leader服务器挂了,那么经历的和初始化流程类似的过程,选择Leader

    Follower服务器挂了,那么自己在执行选举的过程中,会收到其他服务器给的Leader选票信息,也可以确定Leader所属


    楼主热帖排行榜
    搜搜吧社区温馨提示:
    搜搜吧(www.sosoba.org)十分重视网络版权及其他知识产权的保护,针对网络侵权采取如下版权政策:
    1、本站有理由相信网友侵犯任何人的版权或作品,(图文,文字,下载,视频,非法传播),本站有权不事先通知即删除涉嫌侵权的作品和内容!
    2、本站将采取必要的网络技术手段,确认为侵权作品或内容的用户有权进行警告、屏蔽、删除的行为,尽可能的防止侵权行为的发生!
    3、如若您的作品或内容在搜搜吧被侵权,请及时联系我们并提供能证明版权所有的物品,我们将及时进行处理,给您造成不便,敬请谅解!
    4、搜搜吧删帖,投诉,举报,侵权,账号解封唯一指定快速受理频道,请直接发送邮件到 kefu-sosoba@qq.com 一个工作日内核实并邮件通知立即删除
    soso搜搜吧社区是聚合百度搜索,搜狗搜索,360搜索,新闻,教育,站长,广告,娱乐,影视,微信,网盘,营销,手机,汽车,游戏,论坛综合为一体的大型门户社区www.sosoba.org
    Powered by www.sosoba.org X3.4© 2013-2019 搜搜吧社区 小黑屋|手机版|地图|关于我们|新闻资讯|soso搜搜吧社区官网
    搜搜吧社区官网创建于2013年07月23日,本站内容均为会员发表,并不代搜搜吧社区立场,请遵守当地相关法律,客服邮箱: kefu-sosoba@qq.com
    本站所有的内容均来自互联网以及第三方作者自由发布、本站soso搜搜吧不承担任何的法律责任、若有侵权请来信告知,我们在收到举报后的一个工作日内立即删除
    推荐使用:chrmoe谷歌浏览器,搜狗浏览器,QQ浏览器,360极速浏览器,360安全浏览器,猎豹浏览器,火狐浏览器,世界之窗,百度浏览器,Safari浏览器,ios,Android

    GMT+8, 2018-12-12 07:12 , Processed in 1.140625 second(s), 30 queries , Gzip On.

    快速回复 返回顶部 返回列表