Mempool 采用主线程+工作线程的模型,主进程主要是:1创建工作线程 2接受连接并把任务分配给工作线程。
子进程主要做具体的工作:解析请求,处理请求。
一 创建工作线程
先来看源代码
整个过程:
1 初始化主线程的变量
2创建和初始化所有工作线程的资源
3 启动工作线程
4 等待所有工作线程创建完成。
我们可以看到为每个线程创建管道的过程,其它的初始都是在函数setup_thread中完成,我们来看看它具体做些什么事情
整个过程
1 创建libevent,并进行初始化,并加入监听的事件
2 创建工作线程的连接队列
3 初始化互斥锁和后缀缓冲
这样所有的工作线程都运行起来了,各自干各自的活。
问题:
1 为什么主线程需要等到所有工作线程启动完成了才去干自己的事情?
是不是防止下面情况发生: 主线程去接受客户端的请求,并把任务分发给一个工作线程,而该线程又没有完成启动,就会出错??理解错了希望大家指正。
二 主线程接受请求并分发给工作线程
当主线程完成了工作线程的启动,就开始进入自己的正常工作了:循环的调用libevent,是否有连结到来,如果有,那么通过简单的round robin 选择一个线程进行具体的工作。
首先主线程调用server_socket函数,来启动监听的socket。具体在conn_new函数中通过调用event_set把监听事件处理函数设置为 event_handler ,这样当有外部的连接过来的时候,就会直接调用event_handler
而在event_handler中,直接调用drive_machine来处理具体事件。
最后我们来看看drive_machine 干什么事情,其实这个函数就是根据连接的状态进行不同的处理,比如我们连接过来的时候,状态就是 conn_listening:
就是接受连接,并设置socket为非阻塞,最后调用 dispatch_conn_new 处理
从自由链表中获取一个连接,并选择一个工作线程,把连接挂载到工作线程的连接队列中,最后通过管道通知工作线程,工作线程就能触发一个事件,开始工作。
三 工作线程
当主线程通过管道发送一个字节的数据的时候,工作线程通过libevent会触发事件,直接调用thread_libevent_process 函数
这个函数就是读取一个字节数据,防止重复触发事件,再去调用cq_pop ,从线程的等待队列中得到一个连接(socket分装),通过调用conn_new来创建一个连接,在conn_new中主要创建一个连接,并往libevent中添加监听的事件,这样可以监听来自客户端的数据.当有数据来的时候,和前面一样会去调用drive_machine函数,根据连接的状态进行不同的处理。
所以工作线程而言,会触发两类事件,一个来自管道(主线程),一个来自socket(客户端的),前者在刚接受一个新的请求时触发,并且只有一次, 后者有客户端出发,可以多次。并且前者的触发才往libevent中添加后者监听事件,所以后者一定在前者之后。
总结:
1 通知直接使用libevent,简化了异步编程
2 通过主线程和工作线程,使每个角色的任务简单了。主进程和工作线程通过简单的管道实现通信息,保证了系统的高效。
3 工作线程的选择通过简单的 round robin, 对于cache这种业务简单的应用,具有很好的性能。
4 运用很多“池”的方式,提交资源分配效率(会有少量的浪费)
参考
http://www.javaeye.com/topic/344172 Memcached源码分析(线程模型)
http://www.javaeye.com/topic/251488 memcached的通讯层分析