【ZooKeeper】zookeeper源码10-ZooKeeper读写流程源码分析

源码项目zookeeper-3.6.3:核心工作流程

客户端与服务端链接建立

客户端启动和发起链接

ClientCnxn.java
-SendThread
	-run
		-while(state.isAlive()) {
			//如果还没链接上,则建立链接
			if(!clientCnxnSocket.isConnected()) {
				//完成链接: NIO 客户端 链接 NIO 服务端
				startConnect(serverAddress);
			}
			//Session已经链接 超时时间处理/链接超时处理
			if(state.isConnected()) {
				to = readTimeout - clientCnxnSocket.getIdleRecv();
			} else {
				to = connectTimeout - clientCnxnSocket.getIdleRecv();
			}
			//Session 超时了
			if(to <= 0) {
				throw new SessionTimeoutException(warnInfo);
			}
			//心跳处理
			if(state.isConnected()) {
				sendPing();
			}
			//执行读写处理 IO 处理
			clientCnxnSocket.doTransport(to,pendingQueue,ClientCnxn.this);
		}
-primeConnection
	//生成一个 链接请求发送给 服务端
	//其实在这之前,已经完成了,只是再发送一个 连接请求给 ZK Server,建立 Session
	-ConnectRequest conReq = new ConnectRequest(0, lastZxid, sessionTimeout, sessId, sessionPasswd);
	//将链接请求放到 outgoingQueue 队列中,等待发送SendThread 消费该队列,执行队列中的请求对象的发送!
	//由 clientCnxnSocket.doTransport() 来处理
	outgoingQueue.addFirst(new Packet(null,null,conReq,null,null,readonly));
	//注册 OP_READ 和 OP_WRITE 事件
	clientCnxnSocket.connectionPrimed();
	
//通过 startConnect 跟服务端建立链接
-startConnect
	//通过 ClientCnxnSocketNIO 发起链接请求
	-clientCnxnSocket.connect(addr);
	//进入ClientCnxnSocketNIO.java

ClientCnxnSocketNIO.java
-connect
	//创建 NIO 客户端
	-SocketChannel sock = createSock();
	//链接 ZK Server
	-registerAndConnect(sock,addr);
-registerAndConnect
	//注册 OP_CONNECT 事件 客户端注册: OP_CONNECT 服务端注册: OP_ACCEPT
	-sockKey = sock.register(selector, SelectionKey.OP_CONNECT);
	//链接 ZK Server 建立物理链接
	//发起链接,当返回值 immediateConnect 为 true 的时候,意味着链接建立好了。但是还有一些处理
	-boolean immediateConnect = sock.connect(addr);
	//建立逻辑链接
	//因为异步,不一定马上完成链接,成了链接之后,发送链接请求给 zk server,建立 Session
	-if(immediateConnect) {
		sendThread.primeConnection();
		//进入ClientCnxn.java
	}

服务端接收到NIO客户端的链接请求之后,会创建一个NIOServerCnxn对象来专门负责对该Client进行服务。

ClientCnxnSocketNIO.java
-doTransport
	-selector.select(waitTimeOut);
	-Set<SelectionKey> selected;
	//遍历准备就绪的事件,执行处理
	-for(SelectionKey k : selected) {
		SocketChannel sc = ((SocketChannel)k.channel());
		//处理链接请求发送
		if((k.readyOps() & SelectionKey.OP_CONNECT)!=0) sendThread.primeConnection();
		//处理读写请求
		else if((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE))!=0) 
			doIO(pendingQueue, outgoingQueue, cnxn);
-doIO
	//处理服务端返回回来的消息
	-if(sockKey.isReadable())
		sendThread.readResponse(incomingBuffer);
	//处理要发送的消息
	-if(sockKey.isWritable())
		Packet p = findSendablePacket(outgoingQueue, cnxn.sendThread.clientTunneledAuthenticationInProgress());
		p.createBB();
		sock.write(p.bb);
		

客户端的 ConnectRequest 发送到了服务端,服务端 NIOServerCnxn 的 doIO() 方法来进行处理

NIOServerCnxn.java
-doIO
	//处理读
	-if(k.isReadable())
		readPayload();
	//处理写
	-if(k.isWritable())
		//写数据返回客户端
		handleWrite(k);
-readPayload
	//读取链接请求
	-if(!initialized)
		readConnectRequest();
	//读取正常请求: 读写请求
	-else
		readRequest();

-handleWrite
	//写出响应给 客户端
	-int sent = sock.write(directBuffer);

只要服务端对客户端执行请求的处理,或者返回响应,都是调用这个方法;

NIOServerCnxn.java
-doIO(){
// 客户端给服务端写了数据过来
if(sockKey.isReadable()){}
// 服务端给客户端返回响应
if(sockKey.isWritable()){}
}

只要客户端对服务端执行响应的处理,或者发送请求,都是调用这个方法;

ClientCnxnSocketNIO.java
-doIO(){
// 服务端给客户端返回响应
if(sockKey.isReadable()){}
// 客户端给服务端发送请求
if(sockKey.isWritable()){}
}

ZooKeeper会话创建和管理

NIOServerCnxn.java
//读取链接请求
-readConnectRequest
	//处理链接请求
	-zkServer.processConnectRequest(this,incomingBuffer);
	//进入ZooKeeperServer.java

ZooKeeperServer.java
-processConnectRequest
	//第一部分 反序列化恢复得到 ConnectRequest
	-BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(incomingBuffer));
	-ConnectRequest connReq = new ConnectRequest();
	-connReq.deserialize(bia,"connect");
	//第二部分 做 readonly, zxid, sessionTimeOut, sessionID 的处理

	//第三部分 根据 sessionID 来决定是恢复 session 呢,还是创建 Session
	-if(sessionId==0)
		long id = createSession(cnxn,passwd,sessionTimeout);
	-else
		long clientSessionId = connReq.getSessionId();
-createSession
	//通过 SessionTracker 创建 SessionID
	long sessionId = sessionTracker.createSession(timeout);
	//将 NIOServerCnxn 和 Session 一一映射起来
	cnxn.setSessionId(sessionId);
	//提交创建 Sessoin 的请求
	submitRequest(si);

客户端 ClientCnxnSocketNIO 会接收到响应

ClientCnxnSocketNIO.java
//当 SendThread 发送 ConnectRequest 给服务端,服务端完成处理之后,返回 ConnectResponse 给客户端
-doIO
	-if(sockKey.isReadable())
		else if(!initialized)
			//读取链接结果
			readConnectResult();
			//进入ClientCnxnSocket.java

ClientCnxnSocket.java
-readConnectResult
	//从响应对象中,获取服务端返回回来的 SessionID
	-this.sessionId = conRsp.getSessionId();
	//针对 ConnectResponse 执行处理:
	-sendThread.onConnected(conRsp.getTimeOut(),this.sessionId,conRsp.getPasswd(),isRO);
	//进入ClientCnxn.java

ClientCnxn.java
-onConnected
	//告知 hostProvider 此时连接的那一台 host 是 OK 的
	-hostProvider.onConnected();
	//该 ClientCnxn 保存 Session ID
	-sessionId = _sessionId;
	//会话建立好了,则会响应一个监听事件(path:null, event:None, state:SyncConnected)
	-eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None,eventState,null));

1、第一步:ZooKeeper 对象内部的 ClientCnxn 内部的 HostProvider 会随机选一个我们提供的地址,然后委托给 ClientCnxnSocket 去建立和 ZooKeeper 服务端之间的 TCP 链接。
2、第二步:SendThread(Client 的网络发送线程)构造出一个 ConnectRequest 请求(代表客户端与服务器创建一个会话)。同时,Zookeeper 客户端还会进一步将请求包装成网络 IO 的 Packet 对象,放入请求发送队列 outgoingQueue 中去等待发送。
3、 ClientCnxnSocket 从 outgoingQueue 中取出 Packet 对象,将其序列化成 ByteBuffer 后,向服务器进行发送。
4、服务端的 SessionTracker 为该会话分配一个 sessionId,并发送响应。
5、Client 收到响应后,此时此刻便明白自己没有初始化,因此会用 readConnectResult 方法来处理请求。
6、ClientCnxnSocket 会对接受到的服务端响应进行反序列化,得到 ConnectResponse 对象,并从中获取到 Zookeeper 服务端分配的会话 SessionId。
7、通知 SendThread,更新 Client 会话参数(比如重要的 connectTimeout ),并更新 Client 状态;另外,通知地址管理器 HostProvider 当前成功链接的服务器地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值