Zookeeper的Client直接与用户打交道,是我们使用Zookeeper的interface。了解ZK Client的结构和工作原理有利于我们合理的使用ZK,并能在使用中更早的发现问题。本文将在研究源码的技术上讲述ZK Client的工作原理及内部工作机制。

在看完ZK Client的大致架构以后我希望能有一种简单的方式描述ZK Client的基本结构,想来想去我觉得还是图片比较能反映情况,于是我画了这张大致的结构图:

我想既然我画了这张图,就让我们从这张图开始讲起吧。

模块:

我们可以认为ZK的Client由三个主要模块组成:Zookeeper, WatcherManager, ClientCnxn

Zookeeper是ZK Client端的真正接口,用户可以操作的最主要的类,当用户创建一个Zookeeper实例以后,几乎所有的操作都被这个实例包办了,用户不用关心怎么连接到Server,Watcher什么时候被触发等等令人伤神的问题。

WatcherManager,顾名思义,它是用来管理Watcher的,Watcher是ZK的一大特色功能,允许多个Client对一个或多个ZNode进行监控,当ZNode有变化时能够通知到监控这个ZNode的各个Client。我们把一个ZK Client简单看成一个Zookeeper实例,那么这个实例内部的WatcherManager就管理了ZK Client绑定的所有Watcher。

ClientCnxn是管理所有网络IO的模块,所有和ZK Server交互的信息和数据都经过这个模块,包括给ZK Server发送Request,从ZK Server接受Response,以及从ZK Server接受Watcher Event。ClientCnxn完全管理了网络,从外部看来网络操作是透明的。

线程:

每当我们创建一个Zookeeper实例的时候,会有两个线程被创建:SendThread和EventThread。所以当我们使用ZK Client端的时候应该尽量只创建一个Zookeeper实例并反复使用。大量的创建销毁Zookeeper实例不仅会反复的创建和销毁线程,而且会在Server端创建大量的Session。

SendThread是真正处理网络IO的线程,所有通过网络发送和接受的数据包都在这个线程中处理。这个线程的主体是一个while循环:

    while (zooKeeper.state.isAlive()) {
try {
if (sockKey == null) {
// don’t re-establish connection if we are closing
if (closing) {
break;
}
startConnect();
lastSend = now;
lastHeard = now;
}
… ….
selector.select(to);
Set<SelectionKey> selected;
synchronized (this) {
selected = selector.selectedKeys();
}
// Everything below and until we get back to the select is
// non blocking, so time is effectively a constant. That is
// Why we just have to do this once, here
now = System.currentTimeMillis();
for (SelectionKey k : selected) {
… …
if (doIO()) {
lastHeard = now;
}
… …
}
}
catch() {
… …
}
}

这里用了java的nio功能,当selector侦测到事件发生的时候就会触发一次循环,主要的操作会在doIO()里面完成:

    boolean doIO() throws InterruptedException, IOException {
boolean packetReceived = false;
SocketChannel sock = (SocketChannel) sockKey.channel();
if (sock == null) {
throw new IOException(“Socket is null!”);
}
if (sockKey.isReadable()) {
… …
} if (sockKey.isWritable()) {
… …
} if (outgoingQueue.isEmpty()) {
disableWrite();
} else {
enableWrite();
}
return packetReceived;
}

这个过程大概是这样的:

1. 如果有数据可读,则读取数据包,如果数据包是先前发出去的Request的Response,那么这个数据包一定在Pending Queue里面。将它从Pending Queue里面移走,并将此信息添加到Waiting Event Queue 里面,如果数据包是一个Watcher Event,将此信息添加到Waiting Event Queue里面。

2. 如果OutgoingQueue里面有数据需要发送,则发送数据包并把数据包从Outgoing Queue移至Pending Queue,意思是数据我已经发出去了,但还要等待Server端的回复,所以这个请求现在是Pending 的状态。

另外一个线程EventThread是用来处理Event的。前面提到SendThread从Server收到数据的时候会把一些信息添加到Event Thread里面,比如Finish Event和Watcher Event。EventThread就是专门用来处理这些Event的,收到Finish Event的时候会把相对应的Package置成Finish状态,这样等待结果的Client函数就能得以返回。收到Watcher Event的时候会联系WatcherManager找到相对应的Watcher,从WatcherManager里面移除这个Watcher(因为每个Watcher只会被通知一次) 并回调Watcher的process函数。所以所有Watcher的process函数是运行在EventThread里面的。

保持连接:

到目前为止应该已经大概介绍了ZK Client端的大致结构和处理流程。还剩下一个问题就是当网络出问题时ZK Client是如何处理的。其实这个过程并不复杂,大概是执行以下步骤:

1. 网络发生故障,网络操作抛出的异常被捕获。

2. 确认网络操作失败,清除当前与Server相关的网络资源,包括Socket等等。

3. 在Server列表中逐个尝试链接Server。

这个过程从外界看来是透明的,外界并不会觉察到ZK Client已经悄悄地更换了一个连接的Server。

好了,对于ZK Client的介绍大概就这么多了,希望这样的介绍对于大家学习和使用Zookeeper有一些帮助。对于文章中没有介绍或者没有说清楚的地方需要进一步查看源码来解决。

转自 http://www.spnguru.com/2010/08/zookeeper%E5%85%A8%E8%A7%A3%E6%9E%90%E2%80%94%E2%80%94client%E7%AB%AF/

Zookeeper全解析——Client端(转)的更多相关文章

  1. (转)Zookeeper全解析——Paxos作为灵魂

    原计划在介绍完ZK Client之后就着手ZK Server的介绍,但是发现ZK Server所包含的内容实在太多,并不是简简单单一篇Blog就能搞定的.于是决定从基础搞起比较好. 那么ZK Serv ...

  2. Zookeeper全解析——Paxos作为灵魂(转)

    原计划在介绍完ZK Client之后就着手ZK Server的介绍,但是发现ZK Server所包含的内容实在太多,并不是简简单单一篇Blog就能搞定的.于是决定从基础搞起比较好. 那么ZK Serv ...

  3. Zookeeper全解析——Paxos作为灵魂

    原文地址: http://www.spnguru.com/2010/08/zookeeper%E5%85%A8%E8%A7%A3%E6%9E%90%E2%80%94%E2%80%94paxos%E7% ...

  4. zookeeper原理解析-客户端与服务器端交互

    Zookeeper集群中server数量总是确定的,所以集群中的server交互采用比较可靠的bio长连接模型:不同于集群中sever间交互zookeeper客户端其实数量是未知的,为了提高zooke ...

  5. zookeeper原理解析-选举

    1)QuorumPeerMain加载 Zookeeper集群启动的入口类是QuorumPeerMain来加载配置启动QuorumPeer线程.首先我们来看下QuorumPeer, 谷歌翻译quorum ...

  6. 【凯子哥带你学Framework】Activity界面显示全解析

    前几天凯子哥写的Framework层的解析文章<Activity启动过程全解析>,反响还不错,这说明“写让大家都能看懂的Framework解析文章”的思想是基本正确的. 我个人觉得,深入分 ...

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

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

  8. ARM内核全解析,从ARM7,ARM9到Cortex-A7,A8,A9,A12,A15到Cortex-A53,A57

    转自: ARM内核全解析,从ARM7,ARM9到Cortex-A7,A8,A9,A12,A15到Cortex-A53,A57 前不久ARM正式宣布推出新款ARMv8架构的Cortex-A50处理器系列 ...

  9. Oracle AWR报告指标全解析-11011552

    1-5 Top 5 Timed EventsWaits : 该等待事件发生的次数, 对于DB CPU此项不可用Times : 该等待事件消耗的总计时间,单位为秒, 对于DB CPU 而言是前台进程所消 ...

随机推荐

  1. sql server 自增长id 允许插入显示值

    --允许插入显示插入主键id的值SET IDENTITY_INSERT [T0002_SType] ON 执行insert插入语句------------------ --关闭 插入显示值SET ID ...

  2. JSP显示不完全问题

    这个问题出现之后其实有点让我手足无措,因为根本不知道原因出在哪儿. 因为出现这个问题之后修改过一次代码,所以我以为是因为这次修改出现的问题. 但细想之下,这次的修改根本没有涉及到任何有关这方面的东西. ...

  3. iOS开发拓展篇—音频处理(音乐播放器6)

    iOS开发拓展篇—音频处理(音乐播放器6) 一.图片处理 说明: Aspect表示按照原来的宽高比进行缩放. Aspectfit表示按照原来的宽高比缩放,要求看到全部图片,后果是不能完全覆盖窗口,会留 ...

  4. eval解析非标准json

    以前一直在用,但是不知道原理,惭愧啊,今天把自己想法加上. eval("{a:1}"); eval("{a:,b:1}"); 第一眼的感觉是都会得到一个对象,其 ...

  5. MONGODB 与sql聚合操作对应图

    MongoDB 高级查询条件操作符 2012-04-25 15:35:19|  分类: MongoDB |  标签:mongodb使用  mongodb查询  |举报|字号 订阅 http://blo ...

  6. 怎么做一个bat文件

    怎么做一个bat文件 | 浏览:639 | 更新:2014-11-25 17:02 1 2 3 4 5 6 7 分步阅读 其实做一个.BAT文件很简单,下面我就以做一个清除系统垃圾.BAT文件为例,给 ...

  7. Python开发入门与实战10-事务

    1. 事务 本章我们将通过一个例子来简要的说明“事务”,这个开发实战里经常遇到的名词.事务是如何体现在一个具体的业务和系统的实现里. 事务是通过将一组相关操作组合为一个,要么全部成功要么全部失败的单元 ...

  8. sqoop的export的说明

    1.通用参数说明

  9. 2014年4月份第1周51Aspx源码发布详情

    基于Extjs4+MVC4权限管理源码  2014-3-31 [VS2012]源码描述: 20140331更新:修改部门管理中bug 20140303更新:增加部门管理模块,主要包含部门添加,编辑,删 ...

  10. Python显示函数调用堆栈

    网上找到如下几个思路: 1.用inspect模块 2.用sys._getframe模块 3.用sys.exc_traceback,先抛一个异常,然后抓出traceback #!/usr/bin/env ...