首先从Zookeeper入手,Zookeeper-->ClientCnxn-->sendThread/eventThread

  1. public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
  2. boolean canBeReadOnly)
  3. throws IOException
  4. {
  5. LOG.info("Initiating client connection, connectString=" + connectString
  6. + " sessionTimeout=" + sessionTimeout + " watcher=" + watcher);
  7. watchManager.defaultWatcher = watcher;
  8. ConnectStringParser connectStringParser = new ConnectStringParser(
  9. connectString);
  10. HostProvider hostProvider = new StaticHostProvider(
  11. connectStringParser.getServerAddresses());
  12. cnxn = new ClientCnxn(connectStringParser.getChrootPath(),
  13. hostProvider, sessionTimeout, this, watchManager,
  14. getClientCnxnSocket(), canBeReadOnly);
  15. cnxn.start();
  16. }

这里默认使用getClientCnxnSocket(),使用NIO实现ClientCnxnSocketNIO。

cnxn.start();

  1. public void start() {
  2. sendThread.start();
  3. eventThread.start();
  4. }

Zookeeper的APi与Server交互,cnxn.submitRequest提交请求,getData为例,各种xxRequest,xxRespone

  1. public byte[] getData(final String path, Watcher watcher, Stat stat)
  2. throws KeeperException, InterruptedException
  3. {
  4. final String clientPath = path;
  5. PathUtils.validatePath(clientPath);
  6. // the watch contains the un-chroot path
  7. WatchRegistration wcb = null;
  8. if (watcher != null) {
  9. wcb = new DataWatchRegistration(watcher, clientPath);
  10. }
  11. final String serverPath = prependChroot(clientPath);
  12. RequestHeader h = new RequestHeader();
  13. h.setType(ZooDefs.OpCode.getData);
  14. GetDataRequest request = new GetDataRequest();
  15. request.setPath(serverPath);
  16. request.setWatch(watcher != null);
  17. GetDataResponse response = new GetDataResponse();
  18. ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);
  19. if (r.getErr() != 0) {
  20. throw KeeperException.create(KeeperException.Code.get(r.getErr()),
  21. clientPath);
  22. }
  23. if (stat != null) {
  24. DataTree.copyStat(response.getStat(), stat);
  25. }
  26. return response.getData();
  27. }

cnxn.submitRequest先丢到通过queuePacket将请求加入队列outgoingQueue,同步请求阻塞在packet.wait(),这个最后在SendThread.readRespons()-->finishPacket(packet);中被唤醒

  1. public ReplyHeader submitRequest(RequestHeader h, Record request,
  2. Record response, WatchRegistration watchRegistration)
  3. throws InterruptedException {
  4. ReplyHeader r = new ReplyHeader();
  5. Packet packet = queuePacket(h, r, request, response, null, null, null,
  6. null, watchRegistration);
  7. synchronized (packet) {
  8. while (!packet.finished) {
  9. packet.wait();
  10. }
  11. }
  12. return r;
  13. }

加入outgoingQueue队列

  1. Packet queuePacket(RequestHeader h, ReplyHeader r, Record request,
  2. Record response, AsyncCallback cb, String clientPath,
  3. String serverPath, Object ctx, WatchRegistration watchRegistration)
  4. {
  5. Packet packet = null;
  6. // Note that we do not generate the Xid for the packet yet. It is
  7. // generated later at send-time, by an implementation of ClientCnxnSocket::doIO(),
  8. // where the packet is actually sent.
  9. synchronized (outgoingQueue) {
  10. packet = new Packet(h, r, request, response, watchRegistration);
  11. packet.cb = cb;
  12. packet.ctx = ctx;
  13. packet.clientPath = clientPath;
  14. packet.serverPath = serverPath;
  15. if (!state.isAlive() || closing) {
  16. conLossPacket(packet);
  17. } else {
  18. // If the client is asking to close the session then
  19. // mark as closing
  20. if (h.getType() == OpCode.closeSession) {
  21. closing = true;
  22. }
  23. //添加到outgoingQueue发送队列
  24. outgoingQueue.add(packet);
  25. }
  26. }
  27. //唤醒Selector
  28. sendThread.getClientCnxnSocket().wakeupCnxn();
  29. return packet;
  30. }

ClientCnxn的后台线程SendThread负责发送outgoingQueue中的请求,以及接受Server端返回的数据,还包括心跳的发送,run()

先看主要IO流程

clientCnxnSocket.doTransport-->doIO(pendingQueue, outgoingQueue, cnxn)-->sockKey.isWritable()-->sock.write(p.bb)-->sockKey.isReadable()--> sendThread.readResponse(incomingBuffer)--> finishPacket(packet)-->p.notifyAll();

这里在sock.write(p.bb),hui将Packet加入到pendingQueue队列

如果是读取完了之后,自然要从pendingQueue移除remove


  1. clientCnxnSocket.introduce(this,sessionId);
  2. clientCnxnSocket.updateNow();
  3. clientCnxnSocket.updateLastSendAndHeard();
  4. int to;
  5. long lastPingRwServer = System.currentTimeMillis();
  6. while (state.isAlive()) {
  7. try {
  8. if (!clientCnxnSocket.isConnected()) {
  9. if(!isFirstConnect){
  10. try {
  11. Thread.sleep(r.nextInt(1000));
  12. } catch (InterruptedException e) {
  13. LOG.warn("Unexpected exception", e);
  14. }
  15. }
  16. // don't re-establish connection if we are closing
  17. if (closing || !state.isAlive()) {
  18. break;
  19. }
  20. startConnect();
  21. clientCnxnSocket.updateLastSendAndHeard();
  22. }
  23. if (state.isConnected()) {
  24. // determine whether we need to send an AuthFailed event.
  25. if (zooKeeperSaslClient != null) {
  26. boolean sendAuthEvent = false;
  27. if (zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.INITIAL) {
  28. try {
  29. zooKeeperSaslClient.initialize(ClientCnxn.this);
  30. } catch (SaslException e) {
  31. LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e);
  32. state = States.AUTH_FAILED;
  33. sendAuthEvent = true;
  34. }
  35. }
  36. KeeperState authState = zooKeeperSaslClient.getKeeperState();
  37. if (authState != null) {
  38. if (authState == KeeperState.AuthFailed) {
  39. // An authentication error occurred during authentication with the Zookeeper Server.
  40. state = States.AUTH_FAILED;
  41. sendAuthEvent = true;
  42. } else {
  43. if (authState == KeeperState.SaslAuthenticated) {
  44. sendAuthEvent = true;
  45. }
  46. }
  47. }
  48. if (sendAuthEvent == true) {
  49. eventThread.queueEvent(new WatchedEvent(
  50. Watcher.Event.EventType.None,
  51. authState,null));
  52. }
  53. }
  54. to = readTimeout - clientCnxnSocket.getIdleRecv();
  55. } else {
  56. to = connectTimeout - clientCnxnSocket.getIdleRecv();
  57. }
  58. if (to <= 0) {
  59. throw new SessionTimeoutException(
  60. "Client session timed out, have not heard from server in "
  61. + clientCnxnSocket.getIdleRecv() + "ms"
  62. + " for sessionid 0x"
  63. + Long.toHexString(sessionId));
  64. }
  65. if (state.isConnected()) {
  66. int timeToNextPing = readTimeout / 2
  67. - clientCnxnSocket.getIdleSend();
  68. if (timeToNextPing <= 0) {
  69. sendPing();
  70. clientCnxnSocket.updateLastSend();
  71. } else {
  72. if (timeToNextPing < to) {
  73. to = timeToNextPing;
  74. }
  75. }
  76. }
  77. // If we are in read-only mode, seek for read/write server
  78. if (state == States.CONNECTEDREADONLY) {
  79. long now = System.currentTimeMillis();
  80. int idlePingRwServer = (int) (now - lastPingRwServer);
  81. if (idlePingRwServer >= pingRwTimeout) {
  82. lastPingRwServer = now;
  83. idlePingRwServer = 0;
  84. pingRwTimeout =
  85. Math.min(2*pingRwTimeout, maxPingRwTimeout);
  86. pingRwServer();
  87. }
  88. to = Math.min(to, pingRwTimeout - idlePingRwServer);
  89. }
  90. clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);
  91. } catch (Throwable e) {
  92. if (closing) {
  93. if (LOG.isDebugEnabled()) {
  94. // closing so this is expected
  95. LOG.debug("An exception was thrown while closing send thread for session 0x"
  96. + Long.toHexString(getSessionId())
  97. + " : " + e.getMessage());
  98. }
  99. break;
  100. } else {
  101. // this is ugly, you have a better way speak up
  102. if (e instanceof SessionExpiredException) {
  103. LOG.info(e.getMessage() + ", closing socket connection");
  104. } else if (e instanceof SessionTimeoutException) {
  105. LOG.info(e.getMessage() + RETRY_CONN_MSG);
  106. } else if (e instanceof EndOfStreamException) {
  107. LOG.info(e.getMessage() + RETRY_CONN_MSG);
  108. } else if (e instanceof RWServerFoundException) {
  109. LOG.info(e.getMessage());
  110. } else {
  111. LOG.warn(
  112. "Session 0x"
  113. + Long.toHexString(getSessionId())
  114. + " for server "
  115. + clientCnxnSocket.getRemoteSocketAddress()
  116. + ", unexpected error"
  117. + RETRY_CONN_MSG, e);
  118. }
  119. cleanup();
  120. if (state.isAlive()) {
  121. eventThread.queueEvent(new WatchedEvent(
  122. Event.EventType.None,
  123. Event.KeeperState.Disconnected,
  124. null));
  125. }
  126. clientCnxnSocket.updateNow();
  127. clientCnxnSocket.updateLastSendAndHeard();
  128. }
  129. }
  130. }
  131. cleanup();
  132. clientCnxnSocket.close();
  133. if (state.isAlive()) {
  134. eventThread.queueEvent(new WatchedEvent(Event.EventType.None,
  135. Event.KeeperState.Disconnected, null));
  136. }
  137. ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(),
  138. "SendThread exitedloop.");

Zookeeper 3.4.6 Client端流程粗略梳理的更多相关文章

  1. zookeeper 3.4.6启动流程粗略梳理

    zookeeper 3.4.6 启动脚本里面 nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" " ...

  2. Eureka系列(三)获取服务Client端具体实现

    获取服务Client 端流程   我们先看下面这张图片,这张图片简单描述了下我们Client是如何获取到Server已续约实例信息的流程:  从图片中我们可以知晓大致流程就是Client会自己开启一个 ...

  3. Zookeeper全解析——Client端(转)

    Zookeeper的Client直接与用户打交道,是我们使用Zookeeper的interface.了解ZK Client的结构和工作原理有利于我们合理的使用ZK,并能在使用中更早的发现问题.本文将在 ...

  4. 基于Netty和SpringBoot实现一个轻量级RPC框架-Client端请求响应同步化处理

    前提 前置文章: <基于Netty和SpringBoot实现一个轻量级RPC框架-协议篇> <基于Netty和SpringBoot实现一个轻量级RPC框架-Server篇> & ...

  5. ZooKeeper源码阅读——client(二)

    原创技术文章,转载请注明:转自http://newliferen.github.io/ 如何连接ZooKeeper集群   要想了解ZooKeeper客户端实现原理,首先需要关注一下客户端的使用方式, ...

  6. Linux下的C Socket编程 -- 简介与client端的处理

    Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...

  7. Spring Cloud Eureka源码分析 --- client 注册流程

    Eureka Client 是一个Java 客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的.使用轮询负载算法的负载均衡器. 在应用启动后,将会向Eureka Serve ...

  8. BPM端到端流程解决方案分享

    一.需求分析 1.企业规模的不断发展.管理水平的不断提升,通常伴随着企业各业务板块管理分工更细.更专业,IT系统同样越来越多.越来越专 业化.不可避免的,部门墙和信息孤岛出现了,企业的流程被部门或者I ...

  9. Python自动化之rabbitmq rpc client端代码分析(原创)

    RPC调用client端解析 import pika import uuid # 建立连接 class FibonacciRpcClient(object): def __init__(self): ...

随机推荐

  1. C++联合

    原文地址:http://ideage.javaeye.com/blog/210614 联合(union)在C/C++里面见得并不多,但是在一些对内存要求特别严格的地方,联合又是频繁出现,那么究竟什么是 ...

  2. grep -v 排除多人字符串

    # egrep -v '^$|^#' /etc/httpd/conf/httpd.conf # grep -v '^$\|^#' /etc/httpd/conf/httpd.conf

  3. 数据结构和算法 – 10.集合

    集合: 联合.交叉.差异.子集 using System; using System.Collections; using System.Collections.Generic; using Syst ...

  4. MySQL replace函数替换字符串语句的用法(mysql字符串替换)

    MySQL replace函数我们经常用到,下面就为您详细介绍MySQL replace函数的用法,希望对您学习MySQL replace函数方面能有所启迪. 最近在研究CMS,在数据转换的时候需要用 ...

  5. Delphi的字符串、PChar和字符数组之间的转换

    参考:http://my.oschina.net/kavensu/blog/193719 以下的各种方法都是我在Delphi 6的环境下测试成功的,可能根据你的开发环境.不同的上下文语境……有一些可能 ...

  6. HTML CSS微信CSS显示一些总结

    微信显示网页是调用腾讯自带的浏览器内核,由于腾讯浏览器内核对css展示效果没有谷歌浏览器好,导致用谷歌浏览器写好的网页,放到微信页面之后,显示的效果就发生变化,所以调整css样式显得那么吃力: 1. ...

  7. SQLAlchemy Core插入数据,有好几种方法呢

    看是一次插入一条还是多条, 看是数据表名是变量还是常量, 操作还是很灵活的, 主要看哪种顺手顺眼啦. #coding=utf-8 from datetime import datetime from ...

  8. Oracle【IT实验室】数据库备份与恢复之三:OS备份/用户管理的备份与恢复

    用户管理的备份与恢复也称 OS物理备份,是指通过数据库命令设置数据库为备份 状态,然后用操作系统命令,拷贝需要备份或恢复的文件.这种备份与恢复需要用户的 参与手工或自动完成. 对于使用 OS拷贝备份的 ...

  9. 第二十四篇:导出SOUI对象到LUA脚本

    LUA是一种体积小,速度快的脚本语言.脚本语言虽然性能上和C++这样的Naitive语言相比差一点,但是开发速度快,可以方便的更新代码等,近年来受到了越来越多开发者的重视. 在SOUI框架中,我把脚本 ...

  10. Unity3D打Box游戏

    先学习一些基本的脚本实现: 1.动态创建物体.默认位置是(0,0)位置 GameObject goNew = GameObject.CreatePrimitive(PrimitiveType.Cube ...