这里主要分析zookeeper client API的实现方式,以python kazoo的实现代码为蓝本进行逻辑分析.

一.代码框架及介绍

  API分为同步模式和异步模式.同步模式是在异步模式的基础上通过一些等待,循环等方式进行实现的.

  主要实现逻辑如下:

     基本模式就是建立两个线程,一个线程负责发送请求和接收响应.一个负责根据响应执行对应注册的watcher.

     大部分语言的实现都是同步模式通过异步模式实现的.在不同的语言里具体有差异.

  kazoo的框架实现在client,connection,threading,serlallzation这几个主要的类包中.

  client在kazoo项目的根目录,connection,serlallzation在项目的protocol目录,threading在handler目录.

  client是主要类.所有zookeeper 接口均在这里实现.

  connection是核心实现,所有请求和返回的逻辑处理均在这里处理.

  serlallzation是请求封装的实现.将所有请求封装成二进制数据在这里实现.

  threading是线程实现的核心.建立线程,以及启动线程和处理线程均在这里实现.

  https://github.com/python-zk/kazoo/blob/master/kazoo/client.py详细代码可以看这里.

二.详细逻辑的实现.

  1.client的实现

    client是将底层逻辑连接和封装起来的地方,存储公共数据的地方.

    client的代码实现基本是构造各种基础变量,对象,队列,记录当前socket状态,thread状态.重置各种状态.入口方法,和启动,停止等功能.

    简单分析下client下create方法.create方法基本就是检查参数,然后将参数传给create_async(对应的异步方法)方法,create_async将参数通过serlallzation包里的create类封装成request对象.并将这个对象和新的async_object对象传递给入口函数_call._call函数将request对象和async_object对象放到_queue的队列里.之后这个队列由connection里的实现去发送.其他所有zookeeper api的方法都是类是create方法.都是使用同名的async方法通过调用_call来实现.

    connection的对象,threading对象都是在client __init__里初始化的.启动线程是在client的start函数里调用threading.start和connection.start实现.

  2.connection的实现

    这里实现了整个client核心通信的部分.connection通过接收client对象参数,来使用各种client中初始化的公共数据和对象.在connection里核心函数是_connect_attempt函数.这个函数实现了数据交换和通信的核心部分.

    _connect_attempt函数主要部分如下.

            read_timeout, connect_timeout = self._connect(host, port)  #这里是从连接开始的部分贴的,并未将完整的函数贴上.需要完整的请看github
read_timeout = read_timeout / 1000.0 #_connect函数做了主要的握手链接的动作,以及一些错误处理.
connect_timeout = connect_timeout / 1000.0
retry.reset()
self._xid = 0
self.ping_outstanding.clear()
with self._socket_error_handling():
while not close_connection: #这里开始检查是否有主动stop的行为.在connection.stop函数里将这个标志置为真了.
# Watch for something to read or send
jitter_time = random.randint(0, 40) / 100.0
# Ensure our timeout is positive
timeout = max([read_timeout / 2.0 - jitter_time,
jitter_time])
s = self.handler.select([self._socket, self._read_sock],
[], [], timeout)[0] #通过select 来做数据检查等动作. if not s: #这里开始检查select的结果.结果为假的时候即为timeout这个时候发送心跳.
if self.ping_outstanding.is_set():
self.ping_outstanding.clear()
raise ConnectionDropped(
"outstanding heartbeat ping not received")
self._send_ping(connect_timeout) #心跳的主要处理在这里.
elif s[0] == self._socket: #如果结果为可读socket.则进行数据读操作.
response = self._read_socket(read_timeout) #_read_socket 函数里做了返回数据检查.根据返回数据做不同响应.
close_connection = response == CLOSE_RESPONSE #这里检查是否是server发过来的关闭连接的响应.
else:
self._send_request(read_timeout, connect_timeout) #_send_request就是发送请求的部分.client里将请求放入_queue队列里.
self.logger.info('Closing connection to %s:%s', host, port) #就是_send_request函数在消费_queue这个对列的里数据.
client._session_callback(KeeperState.CLOSED)
return STOP_CONNECTING

    _connect_attempt函数有被封装在_connect_loop函数里.这个函数又被zk_loop调用.zk_loop最后在connection.start函数里被装入由threading.spawn函数里创建的线程中一直运行.

    _read_socket函数是触发事件的核心函数.代码如下

    def _read_socket(self, read_timeout):
"""Called when there's something to read on the socket"""
client = self.client header, buffer, offset = self._read_header(read_timeout)
if header.xid == PING_XID:
self.logger.log(BLATHER, 'Received Ping')
self.ping_outstanding.clear()
elif header.xid == AUTH_XID:
self.logger.log(BLATHER, 'Received AUTH') request, async_object, xid = client._pending.popleft()
if header.err:
async_object.set_exception(AuthFailedError())
client._session_callback(KeeperState.AUTH_FAILED)
else:
async_object.set(True)
elif header.xid == WATCH_XID: #在这里通过返回xid判断是否是事件通知.
self._read_watch_event(buffer, offset) #在_read_watch_event函数里将用户设置的函数从client._datawatch队列里放进事件等
else: #待的进程里去.然后由等待事件的进程来执行.用户设置的事件是在_read_sponse函数中被装
self.logger.log(BLATHER, 'Reading for header %r', header) #入对应的列表里的. return self._read_response(header, buffer, offset)

    _read_watch_event函数通过将watch放入handler的事件的对列中去.代码如下.

    def _read_watch_event(self, buffer, offset):
client = self.client
watch, offset = Watch.deserialize(buffer, offset)
path = watch.path self.logger.debug('Received EVENT: %s', watch) watchers = [] if watch.type in (CREATED_EVENT, CHANGED_EVENT): #在这里判断事件类型.并从不同的类型队列中放入watchers列表里.
watchers.extend(client._data_watchers.pop(path, []))
elif watch.type == DELETED_EVENT:
watchers.extend(client._data_watchers.pop(path, []))
watchers.extend(client._child_watchers.pop(path, []))
elif watch.type == CHILD_EVENT:
watchers.extend(client._child_watchers.pop(path, []))
else:
self.logger.warn('Received unknown event %r', watch.type)
return # Strip the chroot if needed
path = client.unchroot(path)
ev = WatchedEvent(EVENT_TYPE_MAP[watch.type], client._state, path) #这个对象是事件函数的参数. # Last check to ignore watches if we've been stopped
if client._stopped.is_set():
return # Dump the watchers to the watch thread
for watch in watchers:
client.handler.dispatch_callback(Callback('watch', watch, (ev,))) #然后通过这个方法将需要触发的事件放入事件线程中监听的队列里.
#从这里可以看出watch函数有一个参数.就是watchedEvent的对象.

    再看看handler.dispatch_callback是如何处理的.代码如下:

def dispatch_callback(self, callback):
"""Dispatch to the callback object
The callback is put on separate queues to run depending on the
type as documented for the :class:`SequentialThreadingHandler`.
"""
self.callback_queue.put(lambda: callback.func(*callback.args)) #可以看见这个函数非常简单.关键就这一句话.就是通过lambda封装成一句话的函数,然后把这个对象放到callback_queue队列里.这个队列在事件线程中循环监听.

    看看事件监听线程是如何做的.代码如下.

def _create_thread_worker(self, queue):  #这个函数在handler.start里被调用.事件的队列也由调用函数传递进来.
def _thread_worker(): # pragma: nocover
while True:
try:
func = queue.get() #这里将事件从队列里拿出来.然后在下面执行.
try:
if func is _STOP:
break
func() #这里执行事件.
except Exception:
log.exception("Exception in worker queue thread")
finally:
queue.task_done()
except self.queue_empty:
continue
t = self.spawn(_thread_worker)
return t
def start(self):
"""Start the worker threads."""
with self._state_change:
if self._running:
return # Spawn our worker threads, we have
# - A callback worker for watch events to be called
# - A completion worker for completion events to be called
for queue in (self.completion_queue, self.callback_queue): #可以看见在这里将callback_queue对列里的对象传递给了_create_thread_worker函数.然后由函数执行.
w = self._create_thread_worker(queue)
self._workers.append(w)
self._running = True
python2atexit.register(self.stop)

    到这里为止,整个事件被触发到装载执行都已经非常清晰了.接下来看看如何被从最初的位置被放进类型队列里的.

            watcher = getattr(request, 'watcher', None)  #这是_read_response的最后一部分代码.可以看见是通过判断request的类型来判断需要触发的事件类型是字节点类型还是数据类型.
if not client._stopped.is_set() and watcher:
if isinstance(request, GetChildren):
client._child_watchers[request.path].add(watcher)
else:
client._data_watchers[request.path].add(watcher)

    基本上kazoo的实现核心逻辑就是这样.通过client将所有共享数据初始化.然后把用户的请求封装成接口.在接口中将请求放入发送队列.然后有connection包类将队列里的请求发送出去.发送出去之后等待服务器响应,并根据响应做不同动作.并且接收到用户这个请求的正常响应之后.将用户注册的事件装入对应的类型队列中.然后等待事件通知到达的时候将事件从类型队列中拿出来放入一直等待执行事件的线程队列中.然后由事件线程执行事件.

    在client的__init__函数里可以清晰的看到这些底层逻辑可以另外提供.只需按照接口名称进行实现即可.

    再详细的细节部分,可以自行参考对应的github

zookeeper client API实现(python kazoo 的实现)的更多相关文章

  1. zookeeper C client API 和zkpython 的安装

    1 zookeeper C API 安装 yum install -y ant 在解压的zookeeper包中执行: ant compile_jute 进入src/c 安装:yum -y instal ...

  2. Zookeeper C API 指南一(转)

    Zookeeper 监视(Watches) 简介 Zookeeper C API 的声明和描述在 include/zookeeper.h 中可以找到,另外大部分的 Zookeeper C API 常量 ...

  3. HBASE的读写以及client API

    一:读写思想 1.系统表 hbase:namespace 存储hbase中所有的namespace的信息 hbase:meta rowkey:hbase中所有表的region的名称 column:re ...

  4. zookeeper集群的python代码测试

    上一篇已经讲解了如何安装zookeeper的python客户端,接下来是我在网上搜到的例子,举例应用环境是: 1.当有两个或者多个服务运行,并且同意时间只有一个服务接受请求(工作),其他服务待命. 2 ...

  5. 9. 使用ZooKeeper Java API编程

    ZooKeeper是用Java开发的,3.4.6版本的Java API文档可以在http://zookeeper.apache.org/doc/r3.4.6/api/index.html上找到. Ti ...

  6. 073 HBASE的读写以及client API

    一:读写思想 1.系统表 hbase:namespace 存储hbase中所有的namespace的信息 hbase:meta rowkey:hbase中所有表的region的名称 column:re ...

  7. Zookeeper系列三:Zookeeper客户端的使用(Zookeeper原生API如何进行调用、ZKClient、Curator)和Zookeeper会话

    一.Zookeeper原生API如何进行调用 准备工作: 首先在新建一个maven项目ZK-Demo,然后在pom.xml里面引入zk的依赖 <dependency> <groupI ...

  8. Elasticsearch Java Rest Client API 整理总结 (一)——Document API

    目录 引言 概述 High REST Client 起步 兼容性 Java Doc 地址 Maven 配置 依赖 初始化 文档 API Index API GET API Exists API Del ...

  9. cloudstack api调用python

    通过python调用cloudstack接口,cloudstack 接口通过  admin   key来判断是否有访问有权访问 #!/usr/bin/env python import urllib2 ...

随机推荐

  1. @PathVariable和@RequestParam的区别,@SessionAttributes

    简介: handler method参数绑定常用的注解,我们根据他们处理的Request的不同内容部分分为四类: A:处理requet uri部分(这里指uri template中variable,不 ...

  2. 关于ONION/Wncry勒索病毒

    1.通告 2017年5月12日起,在国内外网络中发现爆发基于Windows网络共享协议进行攻击传播的蠕虫恶意代码,这是不法分子通过改造之前泄露的NSA黑客武器库中“永恒之蓝”攻击程序发起的网络攻击事件 ...

  3. 【持久化框架】Mybatis简介与原理

    从这篇博文开始我们学习一下Mybatis,希望大家提出宝贵的建议. 什么是Mybatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache softwar ...

  4. angular页面缓存与页面刷新

      angularJS学习笔记:页面缓存与页面刷新 遇到的问题 现在存在这样一个问题,登录前与登录成功后是同一个页面,只不过通过ngIf来控制哪部分显示,图像信息如下: 所以,整体工作不是很难,无非就 ...

  5. Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询

    摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 预见未来最好的方式就是亲手创造未来 – <史蒂夫·乔布斯传> 』 运行环境: ...

  6. UICollection无法下拉刷新的问题

    当UICollectonView加载的内容不够多的时候会出现无法上下拉刷新的问题,折腾了半天,原来是有一个属性没有打开 设置 : self.collectionView.alwaysBounceVer ...

  7. linux开发常用命令

    最近经常查看服务器上的log文件,有时log文件太大查起来很不方便,看了看网上说可以部分查询,就先记录一下吧 Linux中查看部分文件内容命令head,tail,sed的用法: Linux中的查看文件 ...

  8. 5.Lock接口及其实现ReentrantLock

    jdk1.7.0_79 在java.util.concurrent.locks这个包中定义了和synchronized不一样的锁,重入锁——ReentrantLock,读写锁——ReadWriteLo ...

  9. VR全景智慧城市:VR全景技术分析与研究

    全景智慧城市,多年从事三维全景技术应用的互联网公司,我们利用计算机图形学.多媒体.人工智能和计算机网络技术,深入研发和推广虚拟现实9VR0技术的行业应用.如官方网站升级+720度全景.微网站建设+72 ...

  10. 初入计算机图形学——BVH结构的实现

    摘要: 本人水平有限,若有错误也请指正~ 光线追踪作为全局光照解决方案的一个重要思想,其与场景大量三角形的求交效率高低直接影响最终算法的速度,典型的一些渲染器都采用二叉树来将场景进行划分,最近自己实现 ...