zk客户端的ClientCnxn类
ClientCnxn是客户端的类:该类管理zk客户端的socket io,维持一个可用服务器的列表。
//ClientCnxn类
private final LinkedList<Packet> outgoingQueue = new LinkedList<Packet>(); //待发送
private final LinkedList<Packet> pendingQueue = new LinkedList<Packet>(); //发送后等待响应
final SendThread sendThread;
final EventThread eventThread;
Packet封装了请求、响应以及回调等。
static class Packet {
//省略其他代码
RequestHeader requestHeader;
ReplyHeader replyHeader;
Record request;
Record response;
ByteBuffer bb;
String clientPath;
String serverPath;
boolean finished;
AsyncCallback cb;
Object ctx;
WatchRegistration watchRegistration;
public boolean readOnly;
Packet(RequestHeader requestHeader, ReplyHeader replyHeader,
Record request, Record response,
WatchRegistration watchRegistration) {
this(requestHeader, replyHeader, request, response,
watchRegistration, false);
}
Packet(RequestHeader requestHeader, ReplyHeader replyHeader,
Record request, Record response,
WatchRegistration watchRegistration, boolean readOnly) {
this.requestHeader = requestHeader;
this.replyHeader = replyHeader;
this.request = request;
this.response = response;
this.readOnly = readOnly;
this.watchRegistration = watchRegistration;
}
}
ClientCnxn类中有SendThread和EventThread两个线程,SendThread负责io(发送和接收),EventThread负责事件处理。
Packet在outgoingQueue和pendingQueue之间流转:
首先,调用线程把Packet放在outgoingQueue中,SendThread从outgoingQueue中取出Packet并发送,然后把该Packet放入pendingQueue中,等待响应,当响应来到时,解析响应,并从pendingQueue中删除Packet。
示例代码:
public class MyZK {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 60000000, new MyWatcher());
List<ACL> list = new ArrayList<ACL>(1);
list.add(new ACL(Perms.ALL, Ids.ANYONE_ID_UNSAFE));
//CreateResponse
String create = zk.create("/zhang/hello", "hello".getBytes(), list, CreateMode.PERSISTENT);
System.out.println(create);
//GetDataResponse
// 添加了watch。删除该节点的时候,服务端会发通知
byte[] data = zk.getData("/zhang/hello", true, null);
System.out.println(new String(data));
Stat stat = zk.exists("/zhang/hello", false);
System.out.println(stat);
zk.delete("/zhang/hello", -1);
}
static class MyWatcher implements Watcher {
public void process(WatchedEvent event) {
System.out.println(event);
}
}
}
getData 调用栈:

// ZooKeeper.getData
public byte[] getData(final String path, Watcher watcher, Stat stat)
throws KeeperException, InterruptedException
{
final String clientPath = path;
PathUtils.validatePath(clientPath); // the watch contains the un-chroot path
WatchRegistration wcb = null;
if (watcher != null) {
wcb = new DataWatchRegistration(watcher, clientPath);
} final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); //请求头
h.setType(ZooDefs.OpCode.getData);
GetDataRequest request = new GetDataRequest(); //请求
request.setPath(serverPath);
request.setWatch(watcher != null);
GetDataResponse response = new GetDataResponse(); //响应
ReplyHeader r = cnxn.submitRequest(h, request, response, wcb); //发送请求,阻塞,等待响应
if (r.getErr() != 0) {
throw KeeperException.create(KeeperException.Code.get(r.getErr()),
clientPath);
}
if (stat != null) {
DataTree.copyStat(response.getStat(), stat);
}
return response.getData();
}
调用线程把请求放入outgoingQueue队列中,然后阻塞:
public ReplyHeader submitRequest(RequestHeader h, Record request,
Record response, WatchRegistration watchRegistration)
throws InterruptedException {
ReplyHeader r = new ReplyHeader();
//创建Packet,并放入outgoingQueue中
Packet packet = queuePacket(h, r, request, response, null, null, null,
null, watchRegistration);
//先获取锁
synchronized (packet) {
while (!packet.finished) {
//等待,并释放锁
packet.wait();
}
}
return r;
}
SendThread负责io操作,发送请求,接收响应。

发送:把outgoingQueue中的packet发送出去,然后把packet放到pendingQueue中,等待响应。
接收:解析响应,从pendingQueue中删除packet。
// ClientCnxnSocketNIO.doIO
void doIO(List<Packet> pendingQueue, LinkedList<Packet> outgoingQueue, ClientCnxn cnxn)
throws InterruptedException, IOException {
SocketChannel sock = (SocketChannel) sockKey.channel();
if (sock == null) {
throw new IOException("Socket is null!");
}
if (sockKey.isReadable()) {
//读取响应
int rc = sock.read(incomingBuffer);
if (rc < 0) {
throw new EndOfStreamException(
"Unable to read additional data from server sessionid 0x"
+ Long.toHexString(sessionId)
+ ", likely server has closed socket");
}
if (!incomingBuffer.hasRemaining()) {
incomingBuffer.flip();
if (incomingBuffer == lenBuffer) {
recvCount++;
readLength();
} else if (!initialized) {
readConnectResult();
enableRead();
if (findSendablePacket(outgoingQueue,
cnxn.sendThread.clientTunneledAuthenticationInProgress()) != null) {
enableWrite();
}
lenBuffer.clear();
incomingBuffer = lenBuffer;
updateLastHeard();
initialized = true;
} else {
//解析响应
sendThread.readResponse(incomingBuffer);
lenBuffer.clear();
incomingBuffer = lenBuffer;
updateLastHeard();
}
}
}
if (sockKey.isWritable()) {
//发送请求
synchronized(outgoingQueue) {
Packet p = findSendablePacket(outgoingQueue,
cnxn.sendThread.clientTunneledAuthenticationInProgress()); if (p != null) {
updateLastSend();
// If we already started writing p, p.bb will already exist
if (p.bb == null) {
if ((p.requestHeader != null) &&
(p.requestHeader.getType() != OpCode.ping) &&
(p.requestHeader.getType() != OpCode.auth)) {
p.requestHeader.setXid(cnxn.getXid());
}
p.createBB();
}
sock.write(p.bb);
if (!p.bb.hasRemaining()) {
sentCount++;
//从outgoingQueue中删除packet
outgoingQueue.removeFirstOccurrence(p);
if (p.requestHeader != null
&& p.requestHeader.getType() != OpCode.ping
&& p.requestHeader.getType() != OpCode.auth) {
synchronized (pendingQueue) {
//把packet加入到pendingQueue中
pendingQueue.add(p);
}
}
}
}
if (outgoingQueue.isEmpty()) {
disableWrite();
} else if (!initialized && p != null && !p.bb.hasRemaining()) {
disableWrite();
} else {
// Just in case
enableWrite();
}
}
}
}
解析响应:
//SendThread类
void readResponse(ByteBuffer incomingBuffer) throws IOException {
//省略其他代码
ByteBufferInputStream bbis = new ByteBufferInputStream(incomingBuffer);
BinaryInputArchive bbia = BinaryInputArchive.getArchive(bbis);
ReplyHeader replyHdr = new ReplyHeader();
replyHdr.deserialize(bbia, "header"); // -1表示notification
if (replyHdr.getXid() == -1) {
//反序列化,设置WatcherEvent对象
WatcherEvent event = new WatcherEvent();
event.deserialize(bbia, "response");
// convert from a server path to a client path
if (chrootPath != null) {
String serverPath = event.getPath();
if(serverPath.compareTo(chrootPath)==0)
event.setPath("/");
else if (serverPath.length() > chrootPath.length())
event.setPath(serverPath.substring(chrootPath.length()));
else {
LOG.warn("Got server path " + event.getPath()
+ " which is too short for chroot path "
+ chrootPath);
}
} WatchedEvent we = new WatchedEvent(event);
//放入eventThread的队列
eventThread.queueEvent(we);
return;
}
//正常的响应数据
Packet packet;
synchronized (pendingQueue) {
if (pendingQueue.size() == 0) {
throw new IOException("Nothing in the queue, but got " + replyHdr.getXid());
}
packet = pendingQueue.remove();
}
/*
* Since requests are processed in order, we better get a response
* to the first request!
*/
try {
if (packet.requestHeader.getXid() != replyHdr.getXid()) {
packet.replyHeader.setErr(KeeperException.Code.CONNECTIONLOSS.intValue());
throw new IOException("Xid out of order.");
} 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) {
//反序列化设置response内容
packet.response.deserialize(bbia, "response");
}
} finally {
finishPacket(packet);
}
}
调用线程现在还阻塞着呢,需要有人唤醒:
private void finishPacket(Packet p) {
if (p.watchRegistration != null) {
p.watchRegistration.register(p.replyHeader.getErr());
}
if (p.cb == null) {
synchronized (p) {
p.finished = true;
//唤醒调用线程
p.notifyAll();
}
} else {
p.finished = true;
eventThread.queuePacket(p);
}
}
问题:
SendThread是按顺序发送outgoingQueue中的Packet,然后放入pendingQueue中,但是接收到的响应未必是按顺序的(网络环境中先发的数据包未必先到达吧),
zk就这么自信收到的响应和pendingQueue中的顺序一致?
zk客户端的ClientCnxn类的更多相关文章
- ZK客户端脚本的简单使用
sh zkCli.sh [-server ip:port] :连接节点zk客户端[-server ip:port 用于连接集群中指定节点的客户端] 1.创建节点 create [-s] [-e] pa ...
- 利用zk客户端删除solr shard
进入zk客户端 ./bin/zkCli.sh -server ip:2181 显示所有的内容: ls / 删除数据: rmr /filename path
- 第4章 ZK基本特性与基于Linux的ZK客户端命令行学习
第4章 ZK基本特性与基于Linux的ZK客户端命令行学习 4-1 zookeeper常用命令行操作 4-2 session的基本原理与create命令的使用
- zk客户端及锁的使用
1.生成zk客户端对象 private CuratorFramework buildClient() { logger.info("zookeeper registry center ini ...
- ZK客户端
说明:本文为读<从Paxos到Zookeeper 分布式一致性原理与实践>读书笔记 shell操作 Java客户端 原始API pom文件: <dependency> < ...
- 即时聊天IM之四 Android客户端IM帮助类编写
图文无关一起娱乐: 这一篇我们开始写Android端的Smack版主类,后面Android的IM功能都是通过这个帮助类实现的 引用类库: 因为我用的是IDE是Android Studio,所以我通过g ...
- socket系列之客户端socket——Socket类
假设TCP套接字服务器端已经建立好并正在监听客户端的连接了,那么客户端就可以通过Socket类来发起连接.客户端发起一个连接请求后,就被动地在等待服务器的响应.这个类同样位于java.net包中,包含 ...
- dubbo使用的zk客户端
在使用dubbo的过程中,当注册中心的数据修改后,新的配置是怎样刷到consumer和provider的?本文以consumer为例,进行分析. dubbo使用的是zkclient的jar,而zkcl ...
- 第4章 ZK基本特性与基于Linux的ZK客户端命令行学习 4-2 session的基本原理与create命令的使用
客户端与服务端之间存在的连接,那么这样的一个连接我们就称之为会话,也就是session.其实就相当于是我们在做JSP或者说是Service的时候,那么服务端是Servlet,客户端使用的是浏览器.浏览 ...
随机推荐
- JavaScript DOM 元素属性 状态属性
JavaScript DOM 元素属性 状态属性 版权声明:未经允许,严禁转载! 元素的属性 核心 DOM 为我们提供了操作元素标准属性的统一 API. 所有属性节点都储存在元素的 attribute ...
- bzoj 1010 玩具装箱toy -斜率优化
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具 ...
- Python3基础 __doc__ 单行与多行函数文档
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- Python3基础 while 阶乘
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- Python3基础 str endswith 是否以指定字符串结束
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- 【附8】zipkin
一.zipkin作用 全链路追踪工具(查看依赖关系) 查看每个接口.每个service的执行速度(定位问题发生点或者寻找性能瓶颈) 二.zipkin工作原理 创造一些追踪标识符(tracingId,s ...
- 获取String类型汉字乱码,如何进行编码
本文为博主原创,未经允许不得转载: 在解析properties文件中的汉字时,在java代码中解析得到的是一个乱码字符,形如图下: 导致乱码原因:由于在jdk中,默认为gbk编码方式进行编码盒接收的, ...
- jerichotab 初始化页面显示tab页中的第一个
tab初始化默认显示第一个内容,但是tab标签显示最后一个. 源代码: $.fn.initJerichoTab({ renderTo: '#consumable', uniqueId: 'jerich ...
- UVa 1629 切蛋糕(记忆化搜索)
https://vjudge.net/problem/UVA-1629 题意: 有一个n行m列的网格蛋糕上有一些樱桃.每次可以用一刀沿着网格线把蛋糕切成两块,并且只能直切不能拐弯.要求最后每一块蛋糕上 ...
- Cocos2d-x学习笔记(三)main方法
0. UNREFERENCED_PARAMETER(X)避免编译器关于未引用参数的警告. 下边代码用于屏蔽单个函数的警告: #pragma warning( push ) #pragma warnin ...