Zookeeper客户端源码阅读

客户端

客户段交互过程

在这里插入图片描述

客户端使用

	CountDownLatch countDownLatch=new CountDownLatch(1);
	ZooKeeper zooKeeper = new ZooKeeper(CLUSTER_CONNECT_STR, 4000, new Watcher() {
	    @Override
	    public void process(WatchedEvent event) {
	        if(Event.KeeperState.SyncConnected==event.getState()
	                && event.getType()== Event.EventType.None){
	            //如果收到了服务端的响应事件,连接成功
	            countDownLatch.countDown();
	            System.out.println("连接建立");
	        }
	    }
	});
	countDownLatch.await();
	Stat stat = zooKeeper.exists("/user",false);
	if(null ==stat){
	    //创建持久节点
	  	zooKeeper.create("/user","lose_weight".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
	}
	//持久监听
	zooKeeper.addWatch("/user",new Watcher() {
	    @Override
	    public void process(WatchedEvent event) {
	        System.out.println(event);
	    }
	},AddWatchMode.PERSISTENT);
	
	Thread.sleep(Integer.MAX_VALUE);

客户端源码分析

ZooKeeper

	//获取一个ClientCnxn对象,并调用该对象的start方法
	public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher){
		// 解析输入的zookeeper地址
		ConnectStringParser connectStringParser = new ConnectStringParser(connectString);
		// 调用ClientCnxn的new ClientCnxn()方法初始化ClientCnxn对象
		cnxn = createConnection(
		    connectStringParser.getChrootPath(),
		    hostProvider,
		    sessionTimeout,
		    this.clientConfig,
		    watcher,
		    // 获取连接类型,有ClientCnxnSocketNIO(默认)和ClientCnxnSocketNetty两种
		    getClientCnxnSocket(),
		    canBeReadOnly);
		cnxn.start();
	}
	// 对特定目录添加监听
	public void addWatch(String basePath, Watcher watcher, AddWatchMode mode){
		String serverPath = prependChroot(basePath);
        RequestHeader h = new RequestHeader();
        h.setType(ZooDefs.OpCode.addWatch);
        AddWatchRequest request = new AddWatchRequest(serverPath, mode.getMode());
        // submitRequest会等待服务端响应或者响应超时后才返回结果
        ReplyHeader r = cnxn.submitRequest(h, request, new ErrorResponse(),
                new AddWatchRegistration(watcher, basePath, mode));
        if (r.getErr() != 0) {
            throw KeeperException.create(KeeperException.Code.get(r.getErr()),
                    basePath);
        }
	}

ClientCnxn

管理客户端的socket i/o并维护可连接的可用服务器列表,根据需要“透明”地切换所连接的服务器。

	//ClientCnxn的初始化方法
	public ClientCnxn(
	    ......
	    ZKClientConfig clientConfig,
	    Watcher defaultWatcher,
	    ClientCnxnSocket clientCnxnSocket,
	    ......
	) throws IOException {
	    ......
	    // 对建立zookeeper时传入的Watcher的包装
	    this.watchManager = new ZKWatchManager(
	            clientConfig.getBoolean(ZKClientConfig.DISABLE_AUTO_WATCH_RESET),
	            defaultWatcher);
		......
	    this.sendThread = new SendThread(clientCnxnSocket);
	    this.eventThread = new EventThread();
	    // 初始化requestTimeout,如果请求超过这个时间还没有响应,就认为这个响应丢失了
	    initRequestTimeout();
	}
	// ClientCnxn的start方法
	public void start() {
	    sendThread.start();
	    eventThread.start();
	}
	// 将客户端的请求发送到服务端
	public ReplyHeader submitRequest(
        RequestHeader h,
        Record request,
        Record response,
        WatchRegistration watchRegistration,
        WatchDeregistration watchDeregistration) throws InterruptedException {
        ReplyHeader r = new ReplyHeader();
        // 包装请求,并将请求放到outgoingQueue队列中由SendThread发送
        Packet packet = queuePacket(
            h,
            r,
            request,
            response,
            null,
            null,
            null,
            null,
            watchRegistration,
            watchDeregistration);
        // 等待服务端响应
        synchronized (packet) {
            if (requestTimeout > 0) {
                // Wait for request completion with timeout
                waitForPacketFinish(r, packet);
            } else {
                // Wait for request completion infinitely
                while (!packet.finished) {
                    packet.wait();
                }
            }
        }
        if (r.getErr() == Code.REQUESTTIMEOUT.intValue()) {
            sendThread.cleanAndNotifyState();
        }
        return r;
    }
ClientCnxn.SendThread
	public void run() {
		// outgoingQueue来自ClientCnxn,保存需要发送的packetd
		// 将SendThread,sessionId和需要发送的消息的阻塞队列注册到ClientCnxnSocket
		clientCnxnSocket.introduce(this, sessionId, outgoingQueue);
		......
		while (state.isAlive()) {
			......
			// 第一次循环时条件为真
			if (!clientCnxnSocket.isConnected()) {
				......
				// 与服务端建立连接
				startConnect(serverAddress);
				......
			}
			if (state.isConnected()) {
				.......
				// clientCnxnSocket.getIdleRecv()返回从收到上一条消息到发送上一条消息过了多长时间
				to = readTimeout - clientCnxnSocket.getIdleRecv();
			} else {
				to = connectTimeout - clientCnxnSocket.getIdleRecv();
			}
			if (to <= 0) {
				throw new SessionTimeoutException(warnInfo);
			}
			if (state.isConnected()) {
				int timeToNextPing = readTimeout / 2
				                     - clientCnxnSocket.getIdleSend()
				                     - ((clientCnxnSocket.getIdleSend() > 1000) ? 1000 : 0);
				if (timeToNextPing <= 0 || clientCnxnSocket.getIdleSend() > MAX_SEND_PING_INTERVAL) {
					// 发送ping消息
				    sendPing();
				    clientCnxnSocket.updateLastSend();
				} else {
				    if (timeToNextPing < to) {
				        to = timeToNextPing;
				    }
				}
			}
			clientCnxnSocket.doTransport(to, pendingQueue, ClientCnxn.this);
			......
		}
		// 客户端关闭或者权限检验失败逻辑
	}
	private void startConnect(InetSocketAddress addr){
		......
		// 本文中假设实现类为ClientCnxnSocketNetty
		clientCnxnSocket.connect(addr);
	}
	// 处理从server段发来的消息
	void readResponse(ByteBuffer incomingBuffer){
		......
		synchronized (pendingQueue) {
			// 非ping和auth请求在sendThread发送消息时都会加入pendingQueue中
		    if (pendingQueue.size() == 0) {
		        throw new IOException("Nothing in the queue, but got " + replyHdr.getXid());
		    }
		    packet = pendingQueue.remove();
		}
		try {
			// 如果pendingQueue中的第一个消息和响应的中的消息的客户端ID不一致,认为有异常
			// 默认消息响应的顺序与发送的顺序一致
			if (packet.requestHeader.getXid() != replyHdr.getXid()) {
		        packet.replyHeader.setErr(KeeperException.Code.CONNECTIONLOSS.intValue());
		        throw new IOException();
		    }
		    packet.replyHeader.setXid(replyHdr.getXid());
		    packet.replyHeader.setErr(replyHdr.getErr());
		    packet.replyHeader.setZxid(replyHdr.getZxid());
		    if (replyHdr.getZxid() > 0) {
		        lastZxid = replyHdr.getZxid();
		    }
		    if (packet.response != null && replyHdr.getErr() == 0) {
		        packet.response.deserialize(bbia, "response");
		    }
		} finally {
		    finishPacket(packet);
		}
	}
	protected void finishPacket(Packet p) {
	    int err = p.replyHeader.getErr();
	    if (p.watchRegistration != null) {
	        p.watchRegistration.register(err);
	    }
	    // Add all the removed watch events to the event queue, so that the
	    // clients will be notified with 'Data/Child WatchRemoved' event type.
	    if (p.watchDeregistration != null) {
	        Map<EventType, Set<Watcher>> materializedWatchers = null;
	        try {
	        	// 获取Packet对应的watcher
	            materializedWatchers = p.watchDeregistration.unregister(err);
	            for (Entry<EventType, Set<Watcher>> entry : materializedWatchers.entrySet()) {
	                Set<Watcher> watchers = entry.getValue();
	                if (watchers.size() > 0) {
	                	// 把传入的信息包装为WatcherSetEventPair并放入EventThread的waitingEvents队列中,让EventThread处理
	                    queueEvent(p.watchDeregistration.getClientPath(), err, watchers, entry.getKey());
	                    // ignore connectionloss when removing from local
	                    // session
	                    p.replyHeader.setErr(Code.OK.intValue());
	                }
	            }
	        } catch (KeeperException.NoWatcherException nwe) {
	            p.replyHeader.setErr(nwe.code().intValue());
	        } catch (KeeperException ke) {
	            p.replyHeader.setErr(ke.code().intValue());
	        }
	    }
	    // 使用zookeeper.getData方法时,cb为null,getData方法将消息放入outgoing队列后,调用了wait方法,这里需要调用p.notifyAll()来唤醒,表示已经得到了结果
	    if (p.cb == null) {
	        synchronized (p) {
	            p.finished = true;
	            p.notifyAll();
	        }
	    } else {
	        p.finished = true;
	        eventThread.queuePacket(p);
	    }
	}
	
ClientCnxn.EventThread
	// 存放需要处理的event事件的阻塞队列
	private final LinkedBlockingQueue<Object> waitingEvents = new LinkedBlockingQueue<Object>();
	public void run() {
		......
       while (true) {
            Object event = waitingEvents.take();
			......
            processEvent(event);
			......
        }
		......
	}
private void processEvent(Object event) {
	......
	// 服务端对客户端的getData等方法给的响应最后在加入waitingEvents时会包装为WatcherSetEventPair对象,这里就只展示这部分代码
    if (event instanceof WatcherSetEventPair) {
        WatcherSetEventPair pair = (WatcherSetEventPair) event;
        for (Watcher watcher : pair.watchers) {
            try {
                watcher.process(pair.event);
            } catch (Throwable t) {
                LOG.error("Error while calling watcher.", t);
            }
        }
   }
   ......
}

ClientCnxnSocketNetty

	void connect(InetSocketAddress addr){
		// 保证ClientCnxn.SendThread在第一次连接调用doTransport时连接已经建立
		firstConnect = new CountDownLatch(1);
		// handler为ClientCnxnSocketNetty.ZKClientHandler
		Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup)
		                                     .channel(NettyUtils.nioOrEpollSocketChannel())
		                                     .option(ChannelOption.SO_LINGER, -1)
		                                     .option(ChannelOption.TCP_NODELAY, true)
		                                     .handler(new ZKClientPipelineFactory(addr.getHostString(), addr.getPort()));
		bootstrap = configureBootstrapAllocator(bootstrap);
		connectLock.lock();
        try {
            connectFuture = bootstrap.connect(addr);
            // listener的operationComplete方法中调用了firstConnect.countDown()
            connectFuture.addListener(new ChannelFutureListener() {......}
		}
	}
	void doTransport(
		int waitTimeOut,
		Queue<Packet> pendingQueue,
		ClientCnxn cnxn) throws IOException, InterruptedException {
			// 等待与server的连接建立成功,在connect或者onClosing方法中调用firstConnect.countDown()
			if (!firstConnect.await(waitTimeOut, TimeUnit.MILLISECONDS)) {
			    return;
			}
			......
			// 从消息队列中获取需要发送的消息
			head = outgoingQueue.poll(waitTimeOut, TimeUnit.MILLISECONDS);
			......
			if (head != null) {
                doWrite(pendingQueue, head, cnxn);
            }
		}
		......
		// 更新发送消息的时间
		updateNow();
	}
	// 将outgoingQueue中的消息全部发给server,并且将非auth和ping相关的命令保存到pendingQueue队列中
	private void doWrite(Queue<Packet> pendingQueue, Packet p, ClientCnxn cnxn) {
	    updateNow();
	    boolean anyPacketsSent = false;
	    while (true) {
	        if (p != WakeupPacket.getInstance()) {
	            if ((p.requestHeader != null)
	                && (p.requestHeader.getType() != ZooDefs.OpCode.ping)
	                && (p.requestHeader.getType() != ZooDefs.OpCode.auth)) {
	                p.requestHeader.setXid(cnxn.getXid());
	                synchronized (pendingQueue) {
	                    pendingQueue.add(p);
	                }
	            }
	            // 将消息写到channel中,全部写入后再flush
	            sendPktOnly(p);
	            anyPacketsSent = true;
	        }
	        if (outgoingQueue.isEmpty()) {
	            break;
	        }
	        p = outgoingQueue.remove();
	    }
	    // TODO: maybe we should flush in the loop above every N packets/bytes?
	    // But, how do we determine the right value for N ...
	    if (anyPacketsSent) {
	        channel.flush();
	    }
	}
ClientCnxnSocketNetty.ZKClientPipelineFactory

继承自ChannelInitializer,客户端Netty服务的初始化工程

	protected void initChannel(SocketChannel ch) throws Exception {
	    ChannelPipeline pipeline = ch.pipeline();
	    if (clientConfig.getBoolean(ZKClientConfig.SECURE_CLIENT)) {
	        initSSL(pipeline);
	    }
	    pipeline.addLast("handler", new ZKClientHandler());
	}
ClientCnxnSocketNetty.ZKClientHandler

客户端nettry消息的处理类

	protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
	updateNow();
	while (buf.isReadable()) {
	    if (incomingBuffer.remaining() > buf.readableBytes()) {
	        int newLimit = incomingBuffer.position() + buf.readableBytes();
	        incomingBuffer.limit(newLimit);
	    }
	    buf.readBytes(incomingBuffer);
	    incomingBuffer.limit(incomingBuffer.capacity());
	
	    if (!incomingBuffer.hasRemaining()) {
	        incomingBuffer.flip();
	        if (incomingBuffer == lenBuffer) {
	            recvCount.getAndIncrement();
	            readLength();
	        } else if (!initialized) {
	            readConnectResult();
	            lenBuffer.clear();
	            incomingBuffer = lenBuffer;
	            initialized = true;
	            updateLastHeard();
	        } else {
	        	// 处理server段对client的请求的响应消息
	            sendThread.readResponse(incomingBuffer);
	            lenBuffer.clear();
	            incomingBuffer = lenBuffer;
	            updateLastHeard();
	        }
	    }
	}
	// 往outgoingQueue中写入一条消息,唤醒SendThread
	wakeupCnxn();
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值