memcached I/O模型源码分析

      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 fd
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;
    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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值