深入分析Memcached的线程接入模型---上

memcached是一个分布式的内存cache系统,目前被大量地运用于各种各样的站点中,以不断提高站点的总体访问性能,而另外一方面,memcached的使用是非常简单的,可以说,使用门槛很低,这也许是造成memcached目前非常流行的原因之一。

 

我们可以看到,网上分析memcached的文章也比较多,本文是笔者结合memcached源代码的基础上对其线程接入模型进行深入的分析,通过学习和借鉴memcached设计和实现上 的一些思想,为我们的系统架构设计增添新的思路。

 

简单的说,memcached的实现并没有使用多进程模型,而是选择了多线程的模型,可以说,这种实现的方案确实是比较轻量级的。而多线程是基于libevent的事件机制以及基于管道pipe的信令周知机制来实现的。

 

主线程main_thread完成了系统的初始化和生成指定数量的worker_thread后,自身转化为一个dispatch_thread,负责系统的监听和转发,main_thread有自身的event_base 实例,该实例负责监听socket是否有连接到来,如果有,则获取一个新的连接socket并且封装成一个CQ_ITEM对象放入其中某个worker_thread的CQ队列中,以此同时,往该worker_thread的notify pipe写入一个字节以周知该worker_thread已经有新的CQ_ITME到,可以进行处理了。

 

每个worker_thread都有自己的一个CQ队列,该队列存放已经建立连接的各个连接对象,worker_thread正是不断地从各自的CQ队列里面取得各个连接对象进行处理的。同时,worker_thread也有各自的event_base实例用于出来各自的socket读写等操作以及有一对用于信令周知的读写pipe。

 

下面是简单的main_thread和worker_thread示意图:

 

 

 (http://user.qzone.qq.com/270362985?ptlang=2052,这里查看图片)

 

 

 


下面结合源代码(memcached-1.4.4)进行分析,首页看一下关键的数据结构定义。

 

一,存放到每个worker_thread连接队列和连接item的定义

 


view plaincopy to clipboardprint?
/* 
An item in the connection queue.  
封装已经连接的socket file description 
*/ 
 
typedef struct conn_queue_item CQ_ITEM;   
 
struct conn_queue_item {  
 
    int               sfd; //建立连接的socket file description  
 
    enum conn_states  init_state; //连接的状态枚举  
 
    int               event_flags; //事件的flags  
 
    int               read_buffer_size; //读buffer大小  
 
    enum network_transport     transport; //该连接对应的传输协议:TCP或者UDP  
 
    CQ_ITEM          *next; //指向下一个CQ_ITEM的指针  
 
}; 
/*
An item in the connection queue.
封装已经连接的socket file description
*/

typedef struct conn_queue_item CQ_ITEM;

struct conn_queue_item {

    int               sfd; //建立连接的socket file description

    enum conn_states  init_state; //连接的状态枚举

    int               event_flags; //事件的flags

    int               read_buffer_size; //读buffer大小

    enum network_transport     transport; //该连接对应的传输协议:TCP或者UDP

    CQ_ITEM          *next; //指向下一个CQ_ITEM的指针

};

 


 

该CQ_ITEM实例就是main_thread accept一个连接socket后的封装。

 


view plaincopy to clipboardprint?
/* 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;  
 
}; 
/* 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是每个worker_thread存放的已经建立的连接队列,每个worker_thread正是不断的通过读取自己的连接队列的CQ_ITEM进行处理的。

 

二,其实memcached对每个worker_thread也是进行封装成相应的结构体的

为了便于对worker_thread进行处理,memcache对worker_thread进行重新封装,结构体如下:


view plaincopy to clipboardprint?
/* 
 
* tenfyguo: Memcached 对每个线程的封装,每个thread都有自己独立的event_base obj和对应的conn_queue队列 
 
*/ 
 
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 */ 
 
         
 
    struct thread_stats stats;    /* Stats generated by this thread */ 
 
         
 
    struct conn_queue *new_conn_queue; /* queue of new connections to handle */ 
 
   
 
    cache_t *suffix_cache;      /* suffix cache */ 
 
         
 
} LIBEVENT_THREAD; 
/*

* tenfyguo: Memcached 对每个线程的封装,每个thread都有自己独立的event_base obj和对应的conn_queue队列

*/

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 */

      

    struct thread_stats stats;    /* Stats generated by this thread */

      

    struct conn_queue *new_conn_queue; /* queue of new connections to handle */

 

    cache_t *suffix_cache;      /* suffix cache */

      

} LIBEVENT_THREAD;
 


 

      

 

下面是对main_thread作为一个dispatch_thread的封装:

 


view plaincopy to clipboardprint?
typedef struct {  
 
    pthread_t thread_id;        /* unique ID of this thread */ 
 
    struct event_base *base;    /* libevent handle this thread uses */ 
 
} LIBEVENT_DISPATCHER_THREAD; 
typedef struct {

    pthread_t thread_id;        /* unique ID of this thread */

    struct event_base *base;    /* libevent handle this thread uses */

} LIBEVENT_DISPATCHER_THREAD;

 

 

 

 

介绍完主要的数据结构后(当然还有其他重要的数据结构,但跟我们本文要介绍的线程部分联系不是特别紧密,这里暂不扩展介绍了),下面重点介绍各个关键流程。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tenfyguo/archive/2010/01/31/5273828.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值