Twemproxy源码分析(一)启动过程
2015-08-05 09:59
495 查看
概述:
Twemproxy是Twitter开发并开源的适用于Redis和Memcached的轻量级的缓存中间件,可以代理对Redis和Memcached的访问。因为项目需要,我们打算在此基础上进行二次开发,加入一些自己需要的功能(例如cluster之间的数据同步),所以需要深入了解一下Twemproxy源码,我将我阅读Twemproxy源码过程的笔记记录在此。
代码概况:
clone源码:1 | songqi@ubuntu:~/software$ git clone https://github.com/twitter/twemproxy.git |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | songqi@ubuntu:~/software/twemproxy/src$ cloc . 100 text files. 100 unique files. 39 files ignored. http://cloc.sourceforge.net v 1.56 T=0.5 s (122.0 files/s, 41636.0 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- C 34 2894 1684 10985 make 8 281 99 2044 C/C++ Header 19 458 535 1838 ------------------------------------------------------------------------------- SUM: 61 3633 2318 14867 ------------------------------------------------------------------------------- |
事件处理:event/nc_epoll.c、event/nc_event.h、event/nc_evport.c、event/nc_kqueue.c
各种Hash函数:hashkit/nc_crc16.c、hashkit/nc_crc32.c、hashkit/nc_fnv.c、hashkit/nc_hashkit.h、hashkit/nc_hsieh.c、hashkit/nc_jenkins.c、hashkit/nc_ketama.c、hashkit/nc_md5.c、hashkit/nc_modula.c、hashkit/nc_murmur.c、hashkit/nc_one_at_a_time.c、hashkit/nc_random.c
协议:proto/nc_memcache.c、proto/nc_proto.h、proto/nc_redis.c
自定义的数据类型:nc_array.c、nc_array.h、nc_string.c、nc_string.h
网络通信相关:nc_connection.c、nc_connection.h、nc_client.c、nc_client.h、nc_proxy.c、nc_proxy.h
信号处理:nc_signal.c、nc_signal.h
关键数据结构和算法:nc_rbtree.h、nc_rbtree.c、nc_queue.h、nc_request.c、nc_response.c、nc_mbuf.c、nc_mbuf.h、nc_message.c、nc_message.h、nc_server.c、nc_server.h
统计、日志和工具:nc_stats.c、nc_stats.h、nc_log.c、nc_log.h、nc_util.c、nc_util.h
配置文件:nc_conf.c、nc_conf.h
主程序:nc.c、nc_core.c、nc_core.h
以上就是Twemproxy代码的概况。
启动主程序:
我们从main()函数开始看,main()函数在nc.c中,也就是Twemproxy的主程序。Twemproxy的main()函数非常短:12345678910111213141516171819202122232425262728293031323334353637383940414243444546 | intmain(int argc, char **argv){ rstatus_t status; struct instance nci; nc_set_default_options(&nci); status = nc_get_options(argc, argv, &nci); if (status != NC_OK) { nc_show_usage(); exit(1); } if (show_version) { log_stderr("This is nutcracker-%s" CRLF, NC_VERSION_STRING); if (show_help) { nc_show_usage(); } if (describe_stats) { stats_describe(); } exit(0); } if (test_conf) { if (!nc_test_conf(&nci)) { exit(1); } exit(0); } status = nc_pre_run(&nci); if (status != NC_OK) { nc_post_run(&nci); exit(1); } nc_run(&nci); nc_post_run(&nci); exit(1);} |
从argv中获取option,若argv有错误则调用nc_show_usage()打印使用帮助
根据argv,判断是否是:show_version、show_help、describe_stats或者test_conf,是则执行相应逻辑后退出
依次调用nc_pre_run()、nc_run()和nc_post_run()
前面的步骤目前无关紧要,可以看出主要逻辑在nc_pre_run()、nc_run()和nc_post_run()函数中,也就是上面代码中高亮的部分。这三个函数的定义也在nc.c当中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | static rstatus_t nc_pre_run(struct instance *nci) { rstatus_t status; status = log_init(nci->log_level, nci->log_filename); if (status != NC_OK) { return status; } if (daemonize) { status = nc_daemonize(1); if (status != NC_OK) { return status; } } nci->pid = getpid(); status = signal_init(); if (status != NC_OK) { return status; } if (nci->pid_filename) { status = nc_create_pidfile(nci); if (status != NC_OK) { return status; } } nc_print_run(nci); return NC_OK; } |
初始化日志
启动守护进程
初始化信号处理函数
创建pidfile
输出启动信息
如果nc_pre_run()运行失败,main()函数的逻辑中会调用nc_post_run()回收资源
12345678910111213 | static voidnc_post_run(struct instance *nci){ if (nci->pidfile) { nc_remove_pidfile(nci); } signal_deinit(); nc_print_done(); log_deinit();} |
信号处理函数deinit(这里其实什么也不做)
打印关闭信息
日志deinit(关闭日志文件描述符)
接下来就是比较重要的nc_run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static void nc_run(struct instance *nci) { rstatus_t status; struct context *ctx; ctx = core_start(nci); if (ctx == NULL) { return; } /* run rabbit run */ for (;;) { status = core_loop(ctx); if (status != NC_OK) { break; } } core_stop(ctx); } |
调用core_start(),初始化上下文ctx
进入死循环,调用core_loop()进入事件循环
检查退出条件status != NC_OK,退出循环
调用core_stop()结束
可以看出细节都封装到了core_start()、core_loop()和core_stop()当中,但是nc.c主程序的逻辑到这里就结束了。值得一提的是,nc_daemonize()函数封装了创建守护进程的一个标准逻辑。
关键数据结构和数据类型:
程序到这里,涉及到了两个关键的数据结构和数据类型,一个是rstatus_t,一个是instance。rstatus_t的定义在nc_core.h中,与它同时定义的还有err_t
1234567 | #define NC_OK 0#define NC_ERROR -1#define NC_EAGAIN -2#define NC_ENOMEM -3 typedef int rstatus_t; /* return type */typedef int err_t; /* error type */ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | struct instance { struct context *ctx; /* active context */ int log_level; /* log level */ char *log_filename; /* log filename */ char *conf_filename; /* configuration filename */ uint16_t stats_port; /* stats monitoring port */ int stats_interval; /* stats aggregation interval */ char *stats_addr; /* stats monitoring addr */ char hostname[NC_MAXHOSTNAMELEN]; /* hostname */ size_t mbuf_chunk_size; /* mbuf chunk size */ pid_t pid; /* process id */ char *pid_filename; /* pid filename */ unsigned pidfile:1; /* pid file created? */ }; |
小结:
可以看到,Twemproxy的代码量很小,逻辑封装的也比较清晰,读起来是很舒服的。这一段中我们只看了主程序的启动过程,还未深入到细节,细节我们后面再看。这里需要着重记忆的是instance结构体,后面有多处会应用到
相关文章推荐
- poj 1564 Sum It Up 【DFS】【求和为N的所有数的组合】
- Please ensure that adb is correctly located
- VMware联网导出数据
- Java程序员可能犯的3个常见SQL错误
- 大数据探秘之Spark:三、RDD的action操作
- JavaScript为unicode编码转换为中文(转)
- OneProxy主从延迟检测
- django1.8 view(2):URLconf part2
- Java NIO框架Netty教程 (八) Java NIO Selector模式
- 机器学习07(SVM核)
- NYOJ 63 小猴子下落(二叉树)
- android 在一个activity中杀死另外一个Activity
- 《招聘一个靠谱的iOS》面试题参考答案(上)
- Java--多态和虚化
- JS基于FileSystemObject创建一个指定路径的TXT文本文件
- 南邮 OJ 1139 My Summary
- String 的 split 异常:Dangling meta character
- 不敢死队问题
- DAO层、Service层、Controller层、View层
- iOS在照片上添加水印