Zookeeper 前置请求的处理

一、前言

    zookeeper version 3.6

    上篇文章聊到了 zk 的底层线程通信模型,算是为本节 zk 是怎样处理请求的打下了一定的基础,因为如果直接进行 server 端请求的讲解可能会被绕晕,zk 底层模块间的交互基本追求完全的解耦,使用了很多队列,看完上篇文章应当至少对 zk client 端 zk server 端底层队列的使用有个大概的印象。

    Zookeeper 请求准备按照以下三个层级进行介绍:启动前的准备阶段、请求前的协商阶段、请求处理阶段。

二、Zookeeper 启动前的准备阶段与协商阶段

    client 端与 server 端的交互就是通过 org.apache.zookeeper.ZooKeeper 对象,下面看一个简单的案例 

public class TestMain {

    public static void main(String[] args) throws Exception {
        ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 1000, (event) -> {
            System.out.println("connect success");
        });
        zooKeeper.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }
}

    一)client 端           

        启动前的准备阶段与协商阶段就是在创建 Zookeeper 对象的时候完成的,下面看下关键代码

public ZooKeeper(
        String connectString,
        int sessionTimeout,
        Watcher watcher,
        boolean canBeReadOnly,
        HostProvider aHostProvider,
        ZKClientConfig clientConfig) throws IOException {
        
        // 创建 watcher 管理类
        watchManager = defaultWatchManager();
        watchManager.defaultWatcher = watcher;
        
        ConnectStringParser connectStringParser = new ConnectStringParser(connectString);
        hostProvider = aHostProvider;
        
        // 创建连接并初始化 sendThread 和 eventThread 超时时间等
        cnxn = createConnection(
            connectStringParser.getChrootPath(),
            hostProvider,
            sessionTimeout,
            this,
            watchManager,
            getClientCnxnSocket(),
            canBeReadOnly);
        // 连接
        cnxn.start();
    }

            这部分代码主要做了三件事

                1)第一个就是构建 watcher 管理类,这里简单提一下两端的 watcher 管理器,其实 watcher 逻辑主要就是在 client 端维护的,server 端做的事也就是将对应的节点与连接绑定,client 端 watcher 管理器的作用主要就是接收 server 端的事件并且如果 server 端发生异常连接断开 client 端需要将 watcher 重新注册到 server 端。之后会详细介绍 watcher 的机制跟实现原理

                2)解析连接字符串,这个地方会把提供的连接包装成一个对象,因为一个 client 端可以保存集群中多个节点的信息,之后就是创建处理连接的对象

                3)整个阶段的处理逻辑主要在最后 start 方法中,在这里就是启动 SendThread 与 EventThread

        public void run() {
            // 这几行就是记录了下时间 主要就是为发送 ping 作准备
            clientCnxnSocket.introduce(this, sessionId, outgoingQueue);
            clientCnxnSocket.updateNow();
            clientCnxnSocket.updateLastSendAndHeard();
            int to;
            long lastPingRwServer = Time.currentElapsedTime();
            final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds
            InetSocketAddress serverAddress = null;
            while (state.isAlive()) {
                try {
                    // 这些代码是为了处理如果连接失败会怎样
                    if (!clientCnxnSocket.isConnected()) {
                        // don't re-establish connection if we are closing
                        if (closing) {
                            break;
                        }
                        // 如果最近有 ping 成功的机器,则就去连接这个
                        // 如果没有就从一开始提供的地址组里轮询去找
                        if (rwServerAddress != null) {
                            serverAddress = rwServerAddress;
                            rwServerAddress = null;
                        } else {
                            serverAddress = hostProvider.next(1000);
                        }
                        //这里就是连接的逻辑
                        startConnect(serverAddress);
                        clientCnxnSocket.updateLastSendAndHeard();
                    }
                    // 这里是连接成功之后的逻辑
                    // 主要就是安全性相关的处理
                    if (state.isConnected()) {
                        // determine whether we need to send an AuthFailed event.
                        if (zooKeeperSaslClient != null) {
                            boolean sendAuthEvent = false;
                            if (zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.INITIAL) {
                                try {
                                    zooKeeperSaslClient.initialize(ClientCnxn.this);
                                } catch (SaslException e) {
                                    LOG.error("SASL authentication with Zookeeper Quorum member failed.", e);
                                    state = States.AUTH_FAILED;
                                    sendAuthEvent = true;
                                }
                            }
                            KeeperState authState = zooKeeperSaslClient.getKeeperState();
                            if (authState != null) {
                                if (authState == KeeperState.AuthFailed) {
                                    // An authentication error occurred during authentication with the Zookeeper Server.
                                    state = States.AUTH_FAILED;
                                    sendAuthEvent = true;
                                } else {
                                    if (authState == KeeperState.SaslAuthenticated) {
                                        sendAuthEvent = true;
                                    }
                                }
                            }

                            if (sendAuthEvent) {
                                eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, authState, null));
                                if (state == States.AUTH_FAILED) {
                                    eventThread.queueEventOfDeath();
                                }
                            }
                        }
                        to = readTimeout - clientCnxnSocket.getIdleRecv();
                    } else {
                        // 这里记录了下是否超时
                        to = connectTimeout - clientCnxnSocket.getIdleRecv();
                    }

                    if (to <= 0) {
                        String warnInfo = String.format(
                            "Client session timed out, have not heard from server in %dms for session id 0x%s",
                            clientCnxnSocket.getIdleRecv(),
                            Long.toHexString(sessionId));
                        LOG.warn(warnInfo);
                        throw new SessionTimeoutException(warnInfo);
                    }
                    // 这里是连接成功之后会计算下次发送 ping 的时间
                    // 如果需要就会发送 ping 报文
                    if (state.isConnected()) {
                        //1000(1 second) is to prevent race condition missing to send the second ping
                        //also make sure not to send too many pings when readTimeout is small
                        int timeToNextPing = readTimeout / 2
                                             - clientCnxnSocket.getIdleSend()
                                             - ((clientCnxnSocket.getIdleSend() > 1000) ? 1000 : 0);
                        //send a ping request either time is due or no packet sent out within MAX_SEND_PING_INTERVAL
                        if (timeToNextPing <= 0 || clientCnxnSocket.getIdleSend() > MAX_SEND_PING_INTERVAL) {
                            // 发送 ping 报文并更新发送时间
                            sendPing();
                            clientCnxnSocket.updateLastSend();
                        } else {
                            if (timeToNextPing < to) {
                                to = timeToNextPing;
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值