zookeeper客户端C lib代码阅读

添加评论 2011年10月10日

如果想用C/C++进行zookeeper应用开发或者想学习一下linux系统/网络编程,那么阅读一下zookeeper客户端C lib的代码都是很有帮助的
zookeeper提供了完整的C lib给开发者使用,lib完全用C编写,主要涉及linux系统和网络编程的相关知识

代码结构 

 http://labs.renren.com/apache-mirror/zookeeper/ ,  zookeeper-3.4.3.tar.gz\zookeeper-3.4.3\src\c
本文的代码版本为zookeeper-3.3.3,其中客户端C lib的代码在zookeeper-3.3.3/src/c下,下面让我们看看它的代码结构是什么样的(列出的是比较重要的代码)

最关键的代码是当然就是src/zookeeper.c,基本上你编程用到的API都是在这里实现的,函数声明在include/zookeeper.h,这个头文件里面有每个函数的详细解释(可以当文档来看了)
src目录中的mt_adaptor.c和st_adaptor.c是zookeeper.c的好兄弟,辅助zookeeper.c实现相应的功能,他们编译时会被编译成mt、st两个不同的库(一般都是用mt)
其他的文件都是一些周边功能的实现
generated目录下的zookeeper.jute是实现网络传输的序列化和反序列化
include/proto.h自然就是协议的定义
recordio是对zookeeper.jute的序列化封装了一层用i/oarchive结构体来融合所有操作
zk_log是处理log信息的
src目录下的cli.c和load_gen.c是两个使用这个C lib的例子,load_gen中有简单的读写操作,cli.c基本算一个完成的zookeeper客户端了,支持各种操作(想学习zookeeper C编程的强力推荐看一下)
代码目录中还有其他一些代码,如hashtable是处理watch的(我没细看)

代码分析

zhandle
zookeeper客户端所有的代码都是围绕着下面的zhandle进行的,zhandle结构中记录下了客户端与服务器间的所有信息
struct _zhandle {
int fd; /* the descriptor used to talk to zookeeper */
char *hostname; /* the hostname of zookeeper */
struct sockaddr_storage *addrs; /* the addresses that correspond to the hostname */
int addrs_count; /* The number of addresses in the addrs array */
watcher_fn watcher; /* the registered watcher */
struct timeval last_recv; /* The time that the last message was received */
struct timeval last_send; /* The time that the last message was sent */
struct timeval last_ping; /* The time that the last PING was sent */
struct timeval next_deadline; /* The time of the next deadline */
int recv_timeout; /* The maximum amount of time that can go by without
receiving anything from the zookeeper server */
buffer_list_t *input_buffer; /* the current buffer being read in */
buffer_head_t to_process; /* The buffers that have been read and are ready to be processed. */
buffer_head_t to_send; /* The packets queued to send */
completion_head_t sent_requests; /* The outstanding requests */
completion_head_t completions_to_process; /* completions that are ready to run */
int connect_index; /* The index of the address to connect to */
clientid_t client_id;
long long last_zxid;
int outstanding_sync; /* Number of outstanding synchronous requests */
struct _buffer_list primer_buffer; /* The buffer used for the handshake at the start of a connection */
struct prime_struct primer_storage; /* the connect response */
char primer_storage_buffer[40]; /* the true size of primer_storage */
volatile int state;
void *context;
auth_list_head_t auth_h; /* authentication data list */
/* zookeeper_close is not reentrant because it de-allocates the zhandler.
* This guard variable is used to defer the destruction of zhandle till
* right before top-level API call returns to the caller */
int32_t ref_counter;
volatile int close_requested;
void *adaptor_priv;
/* Used for debugging only: non-zero value indicates the time when the zookeeper_process
* call returned while there was at least one unprocessed server response
* available in the socket recv buffer */
struct timeval socket_readable;

zk_hashtable* active_node_watchers;
zk_hashtable* active_exist_watchers;
zk_hashtable* active_child_watchers;
/** used for chroot path at the client side **/
char *chroot;
};
其中有些参数是通过外部传入设置的,如hostname(指定服务器列表,多个用’,’隔开),watcher(指定watcher触发时的回调函数入口),context(传给watcher回调函数的参数)
zhandle中还有几个队列比较关键:to_process ,to_send , sent_requests, completions_to_process,这些都是该lib处理网络请求的输入输出队列,后面在流程分析时可以看到他们的作用

zookeeper_init
zookeeper_init()函数是与服务器建立连接以及相应参数初始化的入口,通过跟入这个函数基本就可以理清这个C lib是如何工作的

zookeeper_init中会malloc出一个zhandle,然后进行相应的初始化,调用getaddrs函数解析hostname(即服务器列表),然后调用adaptor_init
adaptor_init我们只看mt的,首先是队列锁的初始化,然后调用start_threads
start_threads会创建两个线程do_io和do_completion
do_io线程中完成所有与服务器网络通讯的操作,不断循环地调用zookeeper_interest和zookeeper_process函数
zookeeper_interest中就是创建socket连接服务器+设置poll的可读可写(保持可读,to_send队列不空时可写),函数中会检查连接,当连接断掉时会自动重连,服务器不可达时会轮询地连接服务器列表中别的服务器
zookeeper_process先会调用check_events函数(真正地完成读写操作,把读到的数据包丢到to_process队列中),然后从to_process队列中取出数据包根据类型进行相应的操作
do_completion会调用process_completions函数(从completions_to_process队列中取出数据包处理)
总体来看调用zookeeper_init之后就开启了两个线程,与服务器建立连接,数据包就会在输入输出buf和几个队列中运转起来

zoo_get
下面我们通过get操作来看一下这个C lib中是如何实现zookeeper操作的
从zookeeper.h或者zookeeper.c中都可以看到get操作有长得很像的4个函数
zoo_get
zoo_wget
zoo_aget
zoo_awget
其中w表示带watch设置的,a代表异步模式
看一下代码其实都是围绕着zoo_awget这个函数编写的,关键逻辑都在这个里面
zoo_aget是调用zoo_awget时watch参数设成NULL,zoo_wget是同步模式,增加了一个同步标志,等到请求返回结果时函数才返回

小结
这里只是给出了代码的大体思路,想深入理解的话还是要话时间去读,我在读这部分代码的过程中学到了很多linux系统/网络编程的方式与技巧,希望也可以给大家带来帮助,如果有写错的地方大家也可以指出来一起学习下

转载本站文章请注明,转载自:Free.D.V/A[http://ahxgw.tk]
本文标题: 《zookeeper客户端C lib代码阅读》
本文链接: http://ahxgw.tk/archives/zookeeper%e5%ae%a2%e6%88%b7%e7%ab%afc-lib%e4%bb%a3%e7%a0%81%e9%98%85%e8%af%bb

Logo

更多推荐