原文:http://www.iteye.com/topic/344172

下面对memcached的线程模型做下简单分析,先看下memcahced启动时线程处理的流程。 

 memcached的多线程主要是通过实例化多个libevent实现的,分别是一个主线程和n个workers线程 
无论是主线程还是workers线程全部通过libevent管理网络事件,实际上每个线程都是一个单独的libevent实例 

主线程负责监听客户端的建立连接请求,以及accept 连接 
workers线程负责处理已经建立好的连接的读写等事件 

先看一下大致的图示: 

首先看下主要的数据结构(thread.c):

 
  
  1. /* An item in the connection queue. */   
  2. typedef struct conn_queue_item CQ_ITEM;   
  3. struct conn_queue_item {   
  4.     int     sfd;   
  5.     int     init_state;   
  6.     int     event_flags;   
  7.     int     read_buffer_size;   
  8.     int     is_udp;   
  9.     CQ_ITEM *next;   
  10. };   

CQ_ITEM 实际上是主线程accept后返回的已建立连接的fd的封装 

 
  
  1. /* A connection queue. */    
  2. typedef struct conn_queue CQ;    
  3. struct conn_queue {    
  4.     CQ_ITEM *head;    
  5.     CQ_ITEM *tail;    
  6.     pthread_mutex_t lock;    
  7.     pthread_cond_t  cond;    
  8. };  
CQ是一个管理CQ_ITEM的单向链表
 
  
  1. typedef struct {   
  2.     pthread_t thread_id;        /* unique ID of this thread */   
  3.     struct event_base *base;    /* libevent handle this thread uses */   
  4.     struct event notify_event;  /* listen event for notify pipe */   
  5.     int notify_receive_fd;      /* receiving end of notify pipe */   
  6.     int notify_send_fd;         /* sending end of notify pipe */   
  7.     CQ  new_conn_queue;         /* queue of new connections to handle */   
  8. } LIBEVENT_THREAD;   

这是memcached里的线程结构的封装,可以看到每个线程都包含一个CQ队列,一条通知管道pipe 和一个libevent的实例event_base 

另外一个重要的最重要的结构是对每个网络连接的封装conn 

 
  
  1. typedef struct{   
  2.   int sfd;   
  3.   int state;   
  4.   struct event event;   
  5.   short which;   
  6.   char *rbuf;   
  7.   ... //这里省去了很多状态标志和读写buf信息等   
  8. }conn;   

memcached主要通过设置/转换连接的不同状态,来处理事件(核心函数是drive_machine) 

下面看下线程的初始化流程: 
在memcached.c的main函数中,首先对主线程的libevent做了初始化 

 
  
  1. /* initialize main thread libevent instance */    
  2.  main_base = event_init();  
 
  
然后初始化所有的workers线程,并启动,启动过程细节在后面会有描述 
 
  
  1. /* start up worker threads if MT mode */   
  2. thread_init(settings.num_threads, main_base);   

接着主线程调用(这里只分析tcp的情况,目前memcached支持udp方式) 

 
  
  1. server_socket(settings.port, 0)   

这个方法主要是封装了创建监听socket,绑定地址,设置非阻塞模式并注册监听socket的 
libevent 读事件等一系列操作 

然后主线程调用 

 
  
  1. /* enter the event loop */   
  2. event_base_loop(main_base, 0);   
这时主线程启动开始通过libevent来接受外部连接请求,整个启动过程完毕。 

Memcached源码分析--线程模型(二)

Memcached源码分析--线程模型(三)