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"); }}
- memcached I/O模型源码分析
- 重叠I/O模型分析
- Linux--I/O模型分析
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- memcached源码分析-----网络模型
- Memcached源码分析(线程模型)
- Memcached 源码分析--网络模型流程分析
- Overlapped I/O模型深入分析
- Overlapped I/O模型深入分析
- Overlapped I/O模型深入分析
- Overlapped I/O模型深入分析
- php分页和数据表字段设计注意事项
- 从发布者和订阅者来看事件
- 马士兵javaSE小项目chat和Tank单机版总结
- Myeclipse如何使用Maven添加jar包
- 如何配置jdk
- memcached I/O模型源码分析
- hdoj 2089
- 如何在Eclipse下查看JDK源代码
- 为什么一个java源文件中只能有一个public类?
- 基本引用类型和对象引用类型区别
- 蓝桥杯 2014 3 日期差
- 使用vue开发微信公众号下SPA站点的填坑之旅
- HDFS基础知识(设计目标,相关概念,可靠性保障,读写,优缺点)
- 1001. 害死人不偿命的(3n+1)猜想 (15)