原文:http://www.iteye.com/topic/344172
下面对memcached的线程模型做下简单分析,先看下memcahced启动时线程处理的流程。
memcached的多线程主要是通过实例化多个libevent实现的,分别是一个主线程和n个workers线程
无论是主线程还是workers线程全部通过libevent管理网络事件,实际上每个线程都是一个单独的libevent实例
主线程负责监听客户端的建立连接请求,以及accept 连接
workers线程负责处理已经建立好的连接的读写等事件
先看一下大致的图示:
首先看下主要的数据结构(thread.c):
- /* An item in the connection queue. */
- typedef struct conn_queue_item CQ_ITEM;
- struct conn_queue_item {
- int sfd;
- int init_state;
- int event_flags;
- int read_buffer_size;
- int is_udp;
- CQ_ITEM *next;
- };
CQ_ITEM 实际上是主线程accept后返回的已建立连接的fd的封装
- /* A connection queue. */
- typedef struct conn_queue CQ;
- struct conn_queue {
- CQ_ITEM *head;
- CQ_ITEM *tail;
- pthread_mutex_t lock;
- pthread_cond_t cond;
- };
CQ是一个管理CQ_ITEM的单向链表
- typedef struct {
- pthread_t thread_id; /* unique ID of this thread */
- struct event_base *base; /* libevent handle this thread uses */
- struct event notify_event; /* listen event for notify pipe */
- int notify_receive_fd; /* receiving end of notify pipe */
- int notify_send_fd; /* sending end of notify pipe */
- CQ new_conn_queue; /* queue of new connections to handle */
- } LIBEVENT_THREAD;
这是memcached里的线程结构的封装,可以看到每个线程都包含一个CQ队列,一条通知管道pipe 和一个libevent的实例event_base
另外一个重要的最重要的结构是对每个网络连接的封装conn
- typedef struct{
- int sfd;
- int state;
- struct event event;
- short which;
- char *rbuf;
- ... //这里省去了很多状态标志和读写buf信息等
- }conn;
memcached主要通过设置/转换连接的不同状态,来处理事件(核心函数是drive_machine)
下面看下线程的初始化流程:
在memcached.c的main函数中,首先对主线程的libevent做了初始化
- /* initialize main thread libevent instance */
- main_base = event_init();
然后初始化所有的workers线程,并启动,启动过程细节在后面会有描述
- /* start up worker threads if MT mode */
- thread_init(settings.num_threads, main_base);
接着主线程调用(这里只分析tcp的情况,目前memcached支持udp方式)
- server_socket(settings.port, 0)
这个方法主要是封装了创建监听socket,绑定地址,设置非阻塞模式并注册监听socket的
libevent 读事件等一系列操作
然后主线程调用
- /* enter the event loop */
- event_base_loop(main_base, 0);
转载于:https://blog.51cto.com/chenlx/758510