Zookeeper八股文

1.领导者选举

选举的时机:1.集群启动的时候

选举到leader之前,客户端是无法连接的(会被一直断掉),客户端有重试的机制

zxid是日志文件每增加一个就++,所以zxid比较大代表当前节点的数据比较新

每一个节点内部都有一个属性,记录当前节点选谁当leader,另外内部有一个map被当成投票箱使用(内部存的是被选举的节点myid和zxid),节点1如果最先启动,它选的是自己:

 这个时候它的选票发不出去,因为其他节点都还没启动,然后节点2启动,也投给了自己

 然后把选票发给节点1,节点1接到选票后与自己投票箱里的进行pk(不管谁先同步,都需要符合pk原则)

比较原则:先比较zxid,zxid大的胜,如果zxid相同,则比较myid,myid大的胜

所以节点1的投票箱如下:

 投票箱里两个一样的(2,100)第一个是节点1改票改成的,第二个是节点2的选票同步过来的

这个时候它把自己的选票(第一个(2,100))同步给其他节点(目前只有节点2),节点2收到他的选票,发现一样的,不用改票,但是得加到自己的投票箱里,节点2投票箱里的结果如下:

这个时候3个节点中有两个节点已经选了节点2,已经过半,这个时候leader已经选举成功 

这个时候如果节点3启动了,zk1和zk2已经确定了leader是谁,直接同步给它就行了,不用重新选举,但是如果他的数据更加的新(zxid更大)就需要回退,不过旧的话就需要更新,总之会保持跟Leader节点数据同步

 取得过半选票的源码

 

 

2.两阶段提交(2PC)

读请求是根据本机内存里的值进行查询直接返回,不管是Leader还是follower

写请求就不一样了:

1.如果连的是leader节点

(1)leader几点发起两阶段提交里面的第一步预提交:持久化请求,生成日志 

(2)第二阶段的提交

      (2.1)把生成的日志信息发送给follower节点,follower节点接收到日志后持久化日志,持久化成功之后返回ack

      (2.2)接收到超过一半的节点的ack之后进行下一步

      (2.3)真正的提交过程

                (2.3.1)更新自己这台服务器的内存里面的数据

                (2.3.2)发送commit请求命令告诉其他的从节点进行对应的提交

  commit(zxid)是一个异步提交的过程

加到队列里

 

 

2.如果连的是follower节点,把写请求转发给leader

3.两阶段提交中的异常情况

3.客户端怎么操作?怎么保证数据一致性

客户端连接leader节点进行写:

1.生成日志,zxid++ 2. 预提交,发送日志给follwer日志,follower节点收到日志后持久化日志并发送给leader节点ack 3.等着接受ack(收到的ack加上自己超过一半就行了)4.本地提交(更新自己内存数据) 5.提交(发送给commit命令给其他的follower节点,follower收到命令后更新自己内存里面的数据)

尽可能的保证数据的一致性

commit命令是异步提交,是先加到一个队列里,然后有个专门的处理类来处理队列里的命令,leader节点是不会管follower节点有没有接收到commit命令的,直接更新内存,如果follower节点没有接收到就会有数据一致性的问题,大多数情况下是没有问题的,所以就能保证一致性问题

 follower节点增多会提高读的性能,但是因为leader节点接收的ack要过半,也就意味着时间要变长,另外follower节点增多也会导致选举时间增加,这个时候可以创建观察者节点,观察者节点不参加选举也不用接收ack 

  

 观察者节点不参与选举,不参与两阶段提交的同步,可以用来读,是否持久化也是可以配置的

 

 

ZK中的main loop(QuorumPeer.run里面)

try {
    /*
     * Main loop
     */
    while (running) {
        if (unavailableStartTime == 0) {
            unavailableStartTime = Time.currentElapsedTime();
        }

        switch (getPeerState()) {
        case LOOKING:
            LOG.info("LOOKING");
            ServerMetrics.getMetrics().LOOKING_COUNT.add(1);

            if (Boolean.getBoolean("readonlymode.enabled")) {
                LOG.info("Attempting to start ReadOnlyZooKeeperServer");

                // Create read-only server but don't start it immediately
                final ReadOnlyZooKeeperServer roZk = new ReadOnlyZooKeeperServer(logFactory, this, this.zkDb);

                // Instead of starting roZk immediately, wait some grace
                // period before we decide we're partitioned.
                //
                // Thread is used here because otherwise it would require
                // changes in each of election strategy classes which is
                // unnecessary code coupling.
                Thread roZkMgr = new Thread() {
                    public void run() {
                        try {
                            // lower-bound grace period to 2 secs
                            sleep(Math.max(2000, tickTime));
                            if (ServerState.LOOKING.equals(getPeerState())) {
                                roZk.startup();
                            }
                        } catch (InterruptedException e) {
                            LOG.info("Interrupted while attempting to start ReadOnlyZooKeeperServer, not started");
                        } catch (Exception e) {
                            LOG.error("FAILED to start ReadOnlyZooKeeperServer", e);
                        }
                    }
                };
                try {
                    roZkMgr.start();
                    reconfigFlagClear();
                    if (shuttingDownLE) {
                        shuttingDownLE = false;
                        startLeaderElection();
                    }
                    setCurrentVote(makeLEStrategy().lookForLeader());
                } catch (Exception e) {
                    LOG.warn("Unexpected exception", e);
                    setPeerState(ServerState.LOOKING);
                } finally {
                    // If the thread is in the the grace period, interrupt
                    // to come out of waiting.
                    roZkMgr.interrupt();
                    roZk.shutdown();
                }
            } else {
                try {
                    reconfigFlagClear();
                    if (shuttingDownLE) {
                        shuttingDownLE = false;
                        startLeaderElection();
                    }
                    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);
                updateServerState();

                // Add delay jitter before we switch to LOOKING
                // state to reduce the load of ObserverMaster
                if (isRunning()) {
                    Observer.waitForObserverElectionDelay();
                }
            }
            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);
                updateServerState();
            }
            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);
                }
                updateServerState();
            }
            break;
        }
    }
} finally {
    LOG.warn("QuorumPeer main thread exited");
    MBeanRegistry instance = MBeanRegistry.getInstance();
    instance.unregister(jmxQuorumBean);
    instance.unregister(jmxLocalPeerBean);

    for (RemotePeerBean remotePeerBean : jmxRemotePeerBean.values()) {
        instance.unregister(remotePeerBean);
    }

    jmxQuorumBean = null;
    jmxLocalPeerBean = null;
    jmxRemotePeerBean = null;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值