`

memcache的线程模型

 
阅读更多

MC采用一master多worker的工作模型,由master负责accept客户端请求,然后以RR分发给worker;
-t 线程数,用于处理请求,默认为4
-b backlog队列长度,默认1024

线程结构体
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; 
每个线程都包含一个CQ队列,一条通知管道pipe 和一个libevent的实例event_base ;
当master接受新连接后,通过pipe通知worker;
无论是主线程还是workers线程全部通过libevent管理网络事件,实际上每个线程都是一个单独的libevent实例 ;

启动流程
 

 

 
 


代码实现
master调用accept等待客户端连接(accept为非阻塞调用),返回的sfd也同样被设置为非阻塞,之后分发给worker;
下面看看dispatch_conn_new内部是如何进行链接分发的。
void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags, 
                       int read_buffer_size, enum network_transport transport) { 
    CQ_ITEM *item = cqi_new();//创建一个conn_item
    char buf[1]; 
    int tid = (last_thread + 1) % settings.num_threads;//通过round-robin算法选择一个线程

    LIBEVENT_THREAD *thread = threads + tid;//thread数组存储了所有的工作线程 

    cq_push(thread->new_conn_queue, item);//投递item信息到Worker线程的工作队列中 

    buf[0] = 'c'; 
    //在Worker线程的notify_send_fd写入字符c,表示有连接    
    if (write(thread->notify_send_fd, buf, 1) != 1) { 
        perror("Writing to thread notify pipe"); 
    }

worker thread在管道描述符notify_send_fd的回调函数为thread_libevent_process,故一旦有数据到达立刻执行
//子线程会在PIPE管道读上面建立libevent事件,事件回调函数是thread_libevent_process
static void thread_libevent_process(int fd, short which, void *arg) { 
    LIBEVENT_THREAD *me = arg; 
    CQ_ITEM *item; 
    char buf[1]; 
 
    if (read(fd, buf, 1) != 1)//PIPE管道读取一个字节的数据 
        if (settings.verbose > 0) 
            fprintf(stderr, "Can't read from libevent pipe\n"); 
 
    switch (buf[0]) { 
    case 'c'://如果是c,则处理网络连接 
    item = cq_pop(me->new_conn_queue);//从连接队列读出Master线程投递的消息 \

    if (NULL != item) { 
        conn *c = conn_new(item->sfd, item->init_state, item->event_flags, 
                           item->read_buffer_size, item->transport, me->base);//创建连接

conn_new里面会建立sfd的网络监听libevent事件,事件回调函数为event_handler,而event_handler的执行流程最终会进入到业务处理的状态机中。
conn *conn_new(const int sfd, enum conn_states init_state, const int event_flags, 
        const int read_buffer_size, enum network_transport transport, 
        struct event_base *base) 

    conn *c = conn_from_freelist();//获取一个空闲连接,conn是Memcached内部对网络连接的一个封装 
 
    if (NULL == c)//如果没有空闲的连接 
    { 
        if (!(c = (conn *) calloc(1, sizeof(conn))))//申请空间 
        { 
            fprintf(stderr, "calloc()\n"); 
            return NULL; 
        }MEMCACHED_CONN_CREATE(c);

//每个conn都自带读入和输出缓冲区,在进行网络收发数据时,特别方便 
        c->rbuf = (char *) malloc((size_t) c->rsize); 
        c->wbuf = (char *) malloc((size_t) c->wsize); 
        c->ilist = (item **) malloc(sizeof(item *) * c->isize); 

 //建立sfd描述符上面的event事件,事件回调函数为event_handler 
    event_set(&c->event, sfd, event_flags, event_handler, (void *) c); 
    event_base_set(base, &c->event); 
    c->ev_flags = event_flags; 
    if (event_add(&c->event, 0) == -1) 
    {        
       //如果建立libevent事件失败,将创建的conn添加到空闲列表中 
       if (conn_add_to_freelist(c)) 
        { 
            conn_free(c); 
        } 
        perror("event_add"); 
        return NULL; 
    } 

//libevent事件回调函数的处理,回调函数被调用时,表明Memcached监听的端口号有网络事件到了 
void event_handler(const int fd, const short which, void *arg) 

conn *c; 
//进入业务处理状态机 
drive_machine(c); 
 


线程通信
master和worker通过连接队列进行单向通信,即Master接受到新的客户端连接时,将sfd封装到conn_queue_item并投送给woker,Worker从其连接队列中读取conn_queue_item同客户端连接进行交互,详情参见dispatch_conn_new;
struct conn_queue_item { 
    int               sfd;//accept之后的描述符 
    enum conn_states  init_state;//连接的初始状态 
    int               event_flags;//libevent标志 
    int               read_buffer_size;//读取数据缓冲区大小 
    enum network_transport     transport;//内部通信所用的协议 
    CQ_ITEM          *next;//用于实现链表的指针 
}; 

struct conn_queue { 
    CQ_ITEM *head;//头指针,注意这里是单链表,不是双向链表 
    CQ_ITEM *tail;//尾部指针, 
    pthread_mutex_t lock;//锁 
    pthread_cond_t  cond;//条件变量 
}; 

//获取一个连接 
static CQ_ITEM *cq_pop(CQ *cq) { 
    CQ_ITEM *item; 
 
    pthread_mutex_lock(&cq->lock);//执行加锁操作 
    item = cq->head;//获得头部指针指向的数据 
    if (NULL != item) { 
        cq->head = item->next;//更新头指针信息 
        if (NULL == cq->head)//这里为空的话,则尾指针也为空,链表此时为空 
            cq->tail = NULL; 
    }
    pthread_mutex_unlock(&cq->lock);//释放锁操作 
 
    return item; 

  • 大小: 30.1 KB
  • 大小: 61.2 KB
分享到:
评论

相关推荐

    【汇总】Memcache

    1. **高性能**:基于非阻塞I/O模型,采用多线程处理,可以高效地处理大量并发请求。 2. **简单易用**:提供了多种语言的客户端库,如PHP、Java、Python等,方便与各种Web应用集成。 3. **跨平台**:支持多种操作...

    memcacheclient-2.0

    在`memcacheclient-2.0`中,开发人员对数据操作的原子性和顺序性进行了改进,确保了在多线程环境下也能保持数据的一致性,提升了整体服务的质量。 跨平台支持是`memcacheclient-2.0`的一大亮点。为了满足不同操作...

    php_memcache-3.0.9 for php7-nts-vc14-x64 扩展DLL,亲测有效

    非线程安全的PHP通常用于ISAPI(Internet Server Application Programming Interface)扩展模型,这是IIS(Internet Information Services)服务器的一种插件接口。 “vc14”指的是这个扩展是使用Microsoft Visual ...

    php_memcache-3.0.7-5.4-nts-vc9-x86.zip

    这通常是Apache服务器上的默认配置,因为Apache本身是基于进程而非线程的模型。 其次,"vc9"指的是Visual C++ 2008编译器,这是PHP的Windows构建时使用的编译器版本。不同的VC编译器版本可能会影响到PHP与其他库的...

    MemCache和Redis缓存介绍

    - **单线程**:MemCache采用单线程模型,处理每个请求的顺序执行,简化了并发控制,但限制了其并发处理能力。 4. **适用场景**:适合于需要快速响应、数据不需持久化的场景,如动态网站的会话数据、网页静态元素等...

    Memcache的使用和协议分析详解

    此外,Memcache支持多线程并发处理,并采用TCP/IP或Unix域套接字通信协议与客户端交互。 **PHP扩展php_memcache** 对于PHP环境,为了使用Memcache,需要安装php_memcache扩展。这个扩展允许PHP程序与Memcache...

    memcache架构图

    Memcached使用多线程模型处理客户端请求,每个连接对应一个线程,这样可以避免线程上下文切换带来的开销。然而,这也意味着Memcached不适合处理大量并发连接,因为它可能会创建过多的线程。 5. **TCP/IP通信** ...

    Memcache Windows版本,含32位、64位批处理可直接安装

    此外,Memcached支持多线程模型,可以充分利用多核处理器的性能。 综上所述,这个压缩包提供了一套完整的Windows环境下Memcached的安装解决方案,无论是32位还是64位系统都能轻松应对。通过批处理脚本的自动化操作...

    Java开发中的Memcache原理及实现

    - 并发处理:多线程模型,支持多客户端并发请求。 2. Java与Memcached的交互: - 客户端库:Java开发者常用的Memcached客户端库有spymemcached、xmemcached、smemcached等。它们提供了API接口,使得Java应用能够...

    选redis还是memcache

    5. **线程模型**:Memcached采用多线程模型,而Redis采用单线程模型。虽然Redis避免了多线程间的锁冲突,但难以充分利用多核处理器的特性来提高吞吐量。 #### 三、其他考虑因素 1. **代码质量和可读性**:从代码...

    Memcache.doc

    Memcached服务器端的设计基于事件驱动,采用单进程、单线程模型,利用libevent库处理异步I/O事件,这种设计极大地提高了服务的并发处理能力。多个服务器可以协同工作,但它们之间无直接通信,客户端根据数据的键值...

    Redis和Memcache的区别总结

    Redis 使用单线程模型处理客户端请求,虽然这限制了其并发性能,但同时也简化了数据一致性的问题,确保了操作的原子性。而 Memcache 由于没有内置的持久化和复制机制,通常需要使用 CAS(Check and Set)机制来保证...

    memcache源代码分析

    1. **网络通信**:memcached 使用libevent库处理网络事件,这是一个非阻塞I/O模型,能够高效地处理大量的并发连接。libevent通过监听套接字,当有新的连接请求或数据可读时,会触发相应的回调函数,使得memcached...

    Redis day01.pdf

    随着业务需求的增长和技术的演进,Redis在保证其基础设计原则——内存操作的高效性和单线程模型的简洁性的同时,不断地优化和改进,以适应更加复杂和多样的应用场景。这使得Redis在现代的互联网架构中成为不可或缺的...

    神级memcached源代码分析文档_1.4.0代码分析

    Memcached采用线程模型来处理并发请求。主线程负责接收连接并分配给worker线程,worker线程执行实际的请求处理。这种模型简化了并发控制,同时也确保了高并发下的响应效率。 六、分布式哈希算法 客户端的哈希算法...

    Linux+nginx+php+mysql+memcache

    - **特点**:低内存消耗、高效的事件处理模型、配置简单灵活。 - **应用场景**:静态资源服务、反向代理、负载均衡等。 3. **PHP** - **脚本语言**:PHP是一种广泛使用的通用开源脚本语言,尤其适用于Web开发并...

    memcache的tcp_udp_内存流程图.pdf

    本文将基于"memcache的tcp_udp_内存流程图.pdf"这一资料进行详细介绍。 #### 二、系统启动流程与libevent的使用 Memcached在启动过程中使用libevent库来管理事件。libevent是一个高性能的事件处理库,用于实现网络...

    FreeMarkerPro入门例子

    2. **数据模型与模板绑定**:学习如何在Java后端创建数据模型,并将其绑定到FreeMarker模板,以便在前端渲染。这通常通过Spring MVC或类似的框架实现。 3. **模板的加载和解析**:理解FreeMarker如何从文件系统或类...

    最新mmemcached

    5. **线程安全**:Memcached使用单线程模型,避免了线程上下文切换带来的开销,同时通过锁机制确保并发访问的安全性。 在具体使用中,`memcached-1.4.13`是Memcached的一个版本,这个版本可能包含了性能优化、bug...

    redis缓存的学习记录,底层源码学习

    Redis是单线程的,它使用了事件驱动的模型,所有的操作都是异步的。这种设计使得Redis能够快速地处理大量的请求。 六、Redis的数据类型和使用场景 Redis支持多种数据类型,如字符串、哈希表、列表、集合等。每种...

Global site tag (gtag.js) - Google Analytics