memcached I/O模型源码分析

来源:互联网 发布:象棋软件棋力排名 编辑:程序博客网 时间:2024/05/17 01:38

      memcached是基于多路复用实现事件驱动的服务器,在事件处理方面,应用了master+works的线程模型。

      master线程负责监听端口,当有新的连接,master线程负责接收连接,将连接加入到指定的work线程的队列,

然后通过管道通知该线程处理该连接。

     master线程和work线程之间通过一个管道进行信息交互。

     work线程监听管道读端,当master线程发送'c',work线程从队列获取客户端连接进行监听,负责客户端所有

请求的处理。

     总的来说,master线程负责对listen fd的监听,work线程负责对管道fd和client fd的监听。

     memcached的线程处理逻辑图如下所示:


    总的来说,master和work都需要实现事件监听,master监听listen fd,work监听client fd。memcached实现事件

监听是基于libevent网络库。主要的实现流程:

   1)main_base = event_init();初始化事件基地

   2)event_set(event, fd, event_flags, event_handler, args); 设置事件的监听的fd,事件类型,事件处理函数

   3)event_base_set(event_base, event);设置事件指定的事件基地

   4)event_add(event, timeval);将事件加入事件基地的监听队列

   5)event_base_loop(event_base, flag); 进入事件循环

一、main函数

int main (int argc, char **argv) {……main_base = event_init();//初始化主线程的事件基地……    memcached_thread_init(settings.num_threads, main_base);//创建工作线程    ……    if (settings.port && server_sockets(settings.port, tcp_transport,                                           portnumber_file)) {        exit(EX_OSERR);//监听tcp端口    }        if (settings.udpport && server_sockets(settings.udpport, udp_transport,                                              portnumber_file)) {        exit(EX_OSERR);//监听udp端口    }    ……    if (event_base_loop(main_base, 0) != 0) {        retval = EXIT_FAILURE;//进入主线的的事件循环    }}
二、创建工作线程

 1、工作线程结构体

typedef struct {    pthread_t thread_id;        /* 线程id*/    struct event_base *base;    /* 每个线程自有的事件基地 */    struct event notify_event;  /* 监听与master线程的管道的事件 */    int notify_receive_fd;      /* 交互管道的读端 */    int notify_send_fd;         /* 交互管道的写端*/    struct thread_stats stats;  /* Stats generated by this thread */    struct conn_queue *new_conn_queue; /* 主线程分发过来未处理的新连接的队列*/    cache_t *suffix_cache;      /* suffix cache */} LIBEVENT_THREAD;//每个工作线程对应的结构体
2、创建工作线程池的主要流程

    1)创建LIBEVENT_THREAD数组

    2)创建工作线程与主线程的通信管道

    3)创建工作线程的事件基地,添加监听管道的事件

    4)启动工作线程

3、创建工作线程的主函数

void memcached_thread_init(int nthreads, struct event_base *main_base) {    ……//初始化工作线程——LIBEVENT_THREAD数组    threads = calloc(nthreads, sizeof(LIBEVENT_THREAD));    dispatcher_thread.base = main_base;//设置主线程的event_base    dispatcher_thread.thread_id = pthread_self();//设置主线程的线程ID    for (i = 0; i < nthreads; i++) {        ……        //设置工作线程与主线通信的管道        threads[i].notify_receive_fd = fds[0];        threads[i].notify_send_fd = fds[1];        setup_thread(&threads[i]);//设置工作线程的配置        stats.reserved_fds += 5;    }//启动工作线程    for (i = 0; i < nthreads; i++) {        create_worker(worker_libevent, &threads[i]);    }    pthread_mutex_lock(&init_lock);    wait_for_thread_registration(nthreads);//等待所有work线程创建完毕    pthread_mutex_unlock(&init_lock);}
4、设置工作线程的事件模型的函数
static void setup_thread(LIBEVENT_THREAD *me) {    me->base = event_init();//创建工作线程的event_base;    //设置与主线程管道读端的读事件    event_set(&me->notify_event, me->notify_receive_fd,              EV_READ | EV_PERSIST, thread_libevent_process, me);    event_base_set(me->base, &me->notify_event);    if (event_add(&me->notify_event, 0) == -1) {        exit(1);//添加读事件到线程的event_base    }//初始化线程的接收新连接的队列    me->new_conn_queue = malloc(sizeof(struct conn_queue));    cq_init(me->new_conn_queue);    ……}//工作线程notify_send_fd的可读事件的事件处理函数static void thread_libevent_process(int fd, short which, void *arg) {    LIBEVENT_THREAD *me = arg;    ……    if (read(fd, buf, 1) != 1)        ……    switch (buf[0]) {    case 'c'://从线程的新连接队列中获取client fd进行监听    item = cq_pop(me->new_conn_queue);    if (NULL != item) {        //监听新的client fd        conn *c = conn_new(item->sfd, item->init_state, item->event_flags,                           item->read_buffer_size, item->transport, me->base);        ……        c->thread = me;        cqi_free(item);    }        break;    /* we were told to pause and report in */    case 'p':    register_thread_initialized();        break;    }}
5、工作线程的逻辑函数

static void *worker_libevent(void *arg) {    LIBEVENT_THREAD *me = arg;    register_thread_initialized();    event_base_loop(me->base, 0);//进入事件循环,等待与主线程和client的通信    return NULL;}
三、网络通信处理

1、创建socket,监听listen fd

static int server_sockets(int port, enum network_transport transport,                          FILE *portnumber_file) {    if (settings.inter == NULL) {        return server_socket(settings.inter, port, transport, portnumber_file);    }    …… }static int server_socket(const char *interface,                         int port,                         enum network_transport transport,                         FILE *portnumber_file) {//套接字类型设置hints.ai_socktype = IS_UDP(transport) ? SOCK_DGRAM : SOCK_STREAM;……    for (next= ai; next; next= next->ai_next) {        conn *listen_conn_add;        if ((sfd = new_socket(next)) == -1) {            ……            continue;        }        ……//设置socket的选项        if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) {            continue;        } else {            success++;            if (!IS_UDP(transport) && listen(sfd, settings.backlog) == -1) {                ……                return 1;            }            ……        }        if (IS_UDP(transport)) {            int c;//udp连接            for (c = 0; c < settings.num_threads_per_udp; c++) {                int per_thread_fd = c ? dup(sfd) : sfd;                dispatch_conn_new(per_thread_fd, conn_read,                                  EV_READ | EV_PERSIST,                                  UDP_READ_BUFFER_SIZE, transport);            }        } else {//主线程进行端口监听,conn的状态为conn_listening            if (!(listen_conn_add = conn_new(sfd, conn_listening,                                             EV_READ | EV_PERSIST, 1,                                             transport, main_base))) {                 exit(EXIT_FAILURE);            }//监听的端口的fd对应的conn结构体            listen_conn_add->next = listen_conn;            listen_conn = listen_conn_add;        }    }    return success == 0;}

2、监听fd和事件处理

conn_new主要是添加fd到事件基地,主线程监听listen fd和工作线程监听client fd

//添加sfd到事件基地,主要用于监听listen fd和client fdconn *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;    c = conns[sfd];    //conn结构体的属性初始化    ……    //注册sfd的可读事件    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) {        perror("event_add");        return NULL;    }    return c;}
事件处理函数主要是根据c->state的状态执行状态机

/*监听listen fd时,c->state为conn_listening监听client fd时,c->states比较多样化*/void event_handler(const int fd, const short which, void *arg) {    conn *c;    c = (conn *)arg;    ……    drive_machine(c);//关于c->state的状态机处理io事件    return;}

状态机函数,此处只展开关于conn_listening的处理,工作线程中的状态事件的处理会在后续memcached

请求处理中介绍。

static void drive_machine(conn *c) {    ……    while (!stop) {        switch(c->state) {        case conn_listening:            addrlen = sizeof(addr);            ……            sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen);            ……            if (settings.maxconns_fast &&                stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {                ……            } else {            //连接创建成功,分配连接给工作线程                dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,                                     DATA_BUFFER_SIZE, tcp_transport);            }            stop = true;            break;            ……             }    }    return;}

主线程进行新连接的分发,选择一个工作线程,将新连接封装成CQ_ITEM添加到线程的new_conn_queue,

发送‘c’给工作线程,工作线程接收到‘c’会从new_conn_queue获取CQ_ITEM,调用conn_new进行client fd的

监听。

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();    char buf[1];    //选择工作线程来管理新连接    int tid = (last_thread + 1) % settings.num_threads;    LIBEVENT_THREAD *thread = threads + tid;    last_thread = tid;    item->sfd = sfd;    item->init_state = init_state;    item->event_flags = event_flags;    item->read_buffer_size = read_buffer_size;    item->transport = transport;    //将新连接加入到工作线程的新连接队列    cq_push(thread->new_conn_queue, item);    MEMCACHED_CONN_DISPATCH(sfd, thread->thread_id);    buf[0] = 'c';    //发送消息给工作线程,notify_send_fd可读调用thread_libevent_process    if (write(thread->notify_send_fd, buf, 1) != 1) {        perror("Writing to thread notify pipe");    }}


   

  

0 0
原创粉丝点击