zookeeper client源码解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_29493353/article/details/85340935

1.首先是创建zookeeper实例

zk = new ZooKeeper("192.168.0.170:2181", 5000, new zkDemo());

下面是zk的构造函数

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
        boolean canBeReadOnly)
    throws IOException
{
    LOG.info("Initiating client connection, connectString=" + connectString
            + " sessionTimeout=" + sessionTimeout + " watcher=" + watcher);

    watchManager.defaultWatcher = watcher;

    ConnectStringParser connectStringParser = new ConnectStringParser(
            connectString);
    HostProvider hostProvider = new StaticHostProvider(
            connectStringParser.getServerAddresses());
    cnxn = new ClientCnxn(connectStringParser.getChrootPath(),
            hostProvider, sessionTimeout, this, watchManager,
            getClientCnxnSocket(), canBeReadOnly);
    cnxn.start();
}

最主要的是ClientCnxn这个类。其中参数中有getClientCnxnSocket()这个是整个client创建网络传输的地方。

private static ClientCnxnSocket getClientCnxnSocket() throws IOException {
    String clientCnxnSocketName = System
            .getProperty(ZOOKEEPER_CLIENT_CNXN_SOCKET);
    if (clientCnxnSocketName == null) {
        clientCnxnSocketName = ClientCnxnSocketNIO.class.getName();
    }
    try {
        return (ClientCnxnSocket) Class.forName(clientCnxnSocketName)
                .newInstance();
    } catch (Exception e) {
        IOException ioe = new IOException("Couldn't instantiate "
                + clientCnxnSocketName);
        ioe.initCause(e);
        throw ioe;
    }
}

所以一般的话是通过ClientCnxnSocketNIO来创建实例。这个说明服务端是异步io服务器。

之后是 cnxn.start();启动客户端连接。

在启动啥的时候回创建两个线程,一个是sendthread(clientCnxn的内部类sendthread.class),另一个是eventThread。

其中先看sendthread的run方法。

public void run() {
    clientCnxnSocket.introduce(this,sessionId);
    clientCnxnSocket.updateNow();
    clientCnxnSocket.updateLastSendAndHeard();
    int to;
    long lastPingRwServer = System.currentTimeMillis();
    final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds
    while (state.isAlive()) {
        try {
            if (!clientCnxnSocket.isConnected()) {
                if(!isFirstConnect){
                    try {
                        Thread.sleep(r.nextInt(1000));
                    } catch (InterruptedException e) {
                        LOG.warn("Unexpected exception", e);
                    }
                }
                // don't re-establish connection if we are closing
                if (closing || !state.isAlive()) {
                    break;
                }
                startConnect();
                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 == true) {
                        eventThread.queueEvent(new WatchedEvent(
                              Watcher.Event.EventType.None,
                              authState,null));
                    }
                }
                to = readTimeout - clientCnxnSocket.getIdleRecv();
            } else {
                to = connectTimeout - clientCnxnSocket.getIdleRecv();
            }
            
            if (to <= 0) {
                String warnInfo;
                warnInfo = "Client session timed out, have not heard from server in "
                    + clientCnxnSocket.getIdleRecv()
                    + "ms"
                    + " for sessionid 0x"
                    + Long.toHexString(sessionId);
                LOG.warn(warnInfo);
                throw new SessionTimeoutException(warnInfo);
            }
            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) {
                    sendPing();
                    clientCnxnSocket.updateLastSend();
                } else {
                    if (timeToNextPing < to) {
                        to = timeToNextPing;
                    }
                }
            }

            // If we are in read-only mode, seek for read/write server
            if (state == States.CONNECTEDREADONLY) {
                long now = System.currentTimeMillis();
                int idlePingRwServer = (int) (now - lastPingRwServer);
                if (idlePingRwServer >= pingRwTimeout) {
                    lastPingRwServer = now;
                    idlePingRwServer = 0;
                    pingRwTimeout =
                        Math.min(2*pingRwTimeout, maxPingRwTimeout);
                    pingRwServer();
                }
                to = Math.min(to, pingRwTimeout - idlePingRwServer);
            }

            clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);
        } catch (Throwable e) {
            if (closing) {
                if (LOG.isDebugEnabled()) {
                    // closing so this is expected
                    LOG.debug("An exception was thrown while closing send thread for session 0x"
                            + Long.toHexString(getSessionId())
                            + " : " + e.getMessage());
                }
                break;
            } else {
                // this is ugly, you have a better way speak up
                if (e instanceof SessionExpiredException) {
                    LOG.info(e.getMessage() + ", closing socket connection");
                } else if (e instanceof SessionTimeoutException) {
                    LOG.info(e.getMessage() + RETRY_CONN_MSG);
                } else if (e instanceof EndOfStreamException) {
                    LOG.info(e.getMessage() + RETRY_CONN_MSG);
                } else if (e instanceof RWServerFoundException) {
                    LOG.info(e.getMessage());
                } else {
                    LOG.warn(
                            "Session 0x"
                                    + Long.toHexString(getSessionId())
                                    + " for server "
                                    + clientCnxnSocket.getRemoteSocketAddress()
                                    + ", unexpected error"
                                    + RETRY_CONN_MSG, e);
                }
                cleanup();
                if (state.isAlive()) {
                    eventThread.queueEvent(new WatchedEvent(
                            Event.EventType.None,
                            Event.KeeperState.Disconnected,
                            null));
                }
                clientCnxnSocket.updateNow();
                clientCnxnSocket.updateLastSendAndHeard();
            }
        }
    }
    cleanup();
    clientCnxnSocket.close();
    if (state.isAlive()) {
        eventThread.queueEvent(new WatchedEvent(Event.EventType.None,
                Event.KeeperState.Disconnected, null));
    }
    ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(),
            "SendThread exited loop for session: 0x"
                   + Long.toHexString(getSessionId()));
}

其中 clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);为数据传输的关键点。

pendingQueue和outgoingQueue是clientCnxn类的两个全局变量。内部类sendthread.class和eventthread.class都是通过这两个queue来交换数据的。

回到clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this)

扫描二维码关注公众号,回复: 5430752 查看本文章

这个其实是调用clientCnxnSocket的子类ClientCnxnSocketNIO的方法。

 

sockKey.isReadable()是获取服务器处理完的数据,接下来调用了 SendThread.readResponse(**)

方法里面调用了  

eventThread.queueEvent( we );把服务器返回的数据构造成一个watcherevent给eventThread的queue,方便eventThread的异步调用。
public void queueEvent(WatchedEvent event) {
    if (event.getType() == EventType.None
            && sessionState == event.getState()) {
        return;
    }
    sessionState = event.getState();

    // materialize the watchers based on the event
    WatcherSetEventPair pair = new WatcherSetEventPair(
            watcher.materialize(event.getState(), event.getType(),
                    event.getPath()),
                    event);
    // queue the pair (watch set & event) for later processing
    waitingEvents.add(pair);
}

上述代码主要调用了new WatcherSetEventPair( watcher.materialize(event.getState(), event.getType(), event.getPath()), event)构造一个watcher和watchedevent的对象,将这个对象add到queue里面面给eventThread线程不断处理event。

其中watcher.materialize(event.getState(), event.getType(), event.getPath())调用的是zookeeper.java的内部静态类

private static class ZKWatchManager implements ClientWatchManager

这个类是zk的session范围内的watcher管理器。通过这个类来维护不同事件的watcher集合。

所以在sendThread每次获取到response的时候都会 通过这个wathermanager获取znode注册好的watcher(set<watcher>),然后封装成WatcherSetEventPair放到eventqueue中,然后异步执行。

example:

public class zkDemo implements Watcher {
    private static final Logger LOG;
    static {
        //Keep these two lines together to keep the initialization order explicit
        LOG = LoggerFactory.getLogger(ZooKeeper.class);
    }
    private static final CountDownLatch cdl = new CountDownLatch(1);
    static ZooKeeper zk;

    public static void main(String[] args) throws IOException {
        zk = new ZooKeeper("192.168.0.170:2181", 20000, new zkDemo());
        //zk的连接状态
        System.out.println(zk.getState());
        Stat st = new Stat();

        try {
            cdl.await();
        } catch (Exception e) {
            System.out.println("ZK Session established.");
        }
        String path1 = null;
        try {
            Stat stat;
             if((stat = zk.exists( "/zk-test-abz",new zkDemo() )) != null){
                 zk.setData( "/zk-test-abz","567".getBytes(),stat.getVersion() );
             }
            //四个参数 znode路径 data,acl,节点模式(瞬息还是持久还是带自增)

//            zk.close();
//            ZooKeeper zk1 = new ZooKeeper("192.168.0.170:2181", 20000, new zkDemo());
//            System.out.println(new String(zk.getData(path1, true, st)));
//            System.out.println( st.getCzxid() +"," +st.getAversion() );
//            String path2 = zk.create("/zk-test-", "456".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
//            System.out.println(new String(zk.getData(path2, true, st)));
//            System.out.println( st.getCzxid() +"," +st.getAversion() );
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            Thread.sleep( 5000L );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    //监听到事件时进行处理
    public void process(WatchedEvent event) {
        LOG.info( "事件的路径为:" +event.getPath() );
        if (Watcher.Event.KeeperState.SyncConnected == event.getState()) {
            cdl.countDown();
        }
        if (event.getType() == Event.EventType.NodeCreated) {
            System.out.println( event.getPath() + "创建了");
        }
        if (event.getType() == Event.EventType.NodeDataChanged) {
            System.out.println( event.getPath() + "数据改变了");
        }


    }
}

猜你喜欢

转载自blog.csdn.net/qq_29493353/article/details/85340935