zookeeper客户端代码解读

       最近一直在忙WebPageTest(以下简称wpt)开源库的修改工作,其中一项工作需要将zookeeper(以下简称zk)集成到wpt里。zk作为分布式系统的同步工具,实现了写的原子性(要么失败,要么成功,并不存在写一半的情况),并通过“选举组长“和”重选组长“,在负载均衡的同时保证数据一致性。关于zk服务端的设计,可以参考官网http://zookeeper.apache.org/。

       本文阐述zk客户端的实现,一来可以梳理下思路,二来也可作为阅读笔记。

       zk客户端代码比较少,完全采用C语言来实现,包括:


    1、hashtable的实现

       hashtable的实现,包括hashtable.h,hashtable.c,hashtable_itr.h和hashtable_itr.c。从代码中可以看到,hashtable的方式基本是按照JAVA中的hash表实现的。进一步讲,hash表的内部实现其实很简单,如图所示:

zookeeper客户端代码解读_第1张图片

       hashtable内部保存了多个链表(数量可变,当当前节点数超过指定负载时增加)。元素插入,修改,删除时都以hash key为唯一主键,换言之最重要的就是保证hash key的唯一性。元素插入时,hashtable根据key计算出index,并根据index选择将元素插入第几个链表。比如:如果key计算出index为1,那么元素就被插入到list2中;如果当前负载超过最大额度,hashtable自动扩展链表数并对各个链表中的节点重新hash。

       hashtable_itr.h和hashtable_itr.c是hashtable的迭代器,实际上是直接对hashtable中的某个链表进行迭代。


   2、序列化和反序列化

       zk自己实现了序列化,总体而言,zk传输的网络数据都是:包长度(网络大端形式)+包内容。oarchive结构体和iarchive分别包含序列化和反序列化的函数指针,这些函数指针包含最基本的操作,如(反)序列化int,long,字符串(vector,buffer,String)。

       具体协议包头数据(zookeeper.jute.h和zookeeper.jute.c),则根据具体数据类型,调用各个基本函数指针完成。


     3、winconfig.h

       这里需要单独讲一下winconfig.h。在讲zk继承到wpt时,我发现编译报错HMONITOR__ type redefinition,进一步排查发现与原来是这个文件定义的类型和wpt中相冲突,在各个宏定义之前加上#ifndef判断之后,编译成功。


    4、核心实现

      核心实现中_zhandle封装zk客户端的所有数据,包括zk_adaptor.h,mt_adaptor.c

      1)IO线程。IO线程负责socket的接收或发送。发送时数据被写入到to_send链表,zk随后通过select或poll完成。如果是异步任务(需要回调),则同时创建回调并将回调数据加入到sent_requests链表。因为zk服务端保证了任务的有序性(顺序请求一定也是顺序返回),所以当zk服务端任务结果到来,IO线程将任务结果写入to_process链表中,那么to_process中的结果和sent_requests中的回调就是对应的。

      同步调用和异步调用流程一致,不同的是同步调用没有回调函数(实际上回调函数被指定为SYNCHRONOUS_MARKER),该标识会导致Complete线程做特殊处理。因为异步流程是没有等待的,所以同步时函数进入等待状态(等待任务标记完成)。

      接下来就很简单了:zk将二者统一发送给Complete线程去处理。我想zk通过顺序来确定回调,应该是为了避免在发送数据中记录回调的信息。

      2)Complete线程。Complete线程很简单,就是IO线程的消费者:调用回调函数。如果回调函数为SYNCHRONOUS_MARKER,则Complete通知等待线程任务完成。


你可能感兴趣的:(c/c++)