[QA]服务端进程模型

问题来自我在segmentfault上提出的问题

先描述一下问题背景: 研究过Reactor的同学都知道epoll,小弟最近在写一个Reactor, 然后有一个地方很困惑,以前也想过这个方式。代码如下:

 请输入图片描述

这里是反应堆的核心代码,箭头标明的地方就是我的问题所在。 在epoll返回一个描述符可读或者可写的时候我会把对应的事件放入到一个singalQueue,然后返回后由主进程统一执行对应事件上的回调。因为是无阻塞的,所以这里的处理应该是相当快的。

以前也想过将就绪事件放入到一个线程池里面,由Worker线程异步处理(Nginx使用的是每个进程一个epoll监听并同步处理)。这样主进程直接重新进入epoll_wait, 但是这样会遇到一些问题:

1.就Linux本身的线程模型来看,用户空间和内核空间是1:1的,线程在底层其实和进程是一样的,那么对同一个事件而言,有可能第一次就绪后是由线程1处理,第二次由线程2处理,频繁的切换线程对于同一个套接口描述符的读写会不会引起CPU缓存抖动?

<del>2.和上一个问题很类似,考虑到网卡的中断亲和性设置,就有可能是处理中断的时候是CPU—0,而在用户层这个数据是被线程1执行的,线程1却在CPU-1上执行了读取操作,也就是说可能导致的情况就是,处理中断的CPU和从网卡读取数据的CPU不是同一个,这种情况下SMP体系的CPU的BUFFER是怎么安排的?</del>

以下是我对这个问题的总结:

第一个问题就是由于担心一次性返回的fd过多,main进程负责处理过多的IO导致其他链接等待时间过长,所以才去线程池来处理IO和逻辑。

以前在考虑epoll返回后的fd的时候只考虑到了IO的处理是应该由主进程来出来还是放到线程中来处理

关于IO抖动的问题同样有来自 @felix021 大神的回答:

   对于问题1,如果是针对每个线程开一个队列,用个哈希算法把事件固定地分配给每个线程,在请求数较多的情况下,统计上说各核的利用率应该也会比较平均,这样你的问题应该基本可以解决,而且锁的粒度也还顺便减小了;只是实际效果会不会改善不好说。。。

这里利用hash把每个连接固定的放到某个线程池的任务队列中去,不失为一个好办法,但是同样的对于底层的IO是否应该使用多线程我尚不能得到一个满意的回答(朋友说不要使用,原因也是CPU缓存抖动)

在看陈硕大哥的文章后有了新一些的认识

这张图是陈硕大哥总结的进程模型图

 

reactor_comparison

第五种model就是我问题中描述的那种,主线程reactor负责IO,逻辑处理(计算).

第六种model是为每一个连接创建一个逻辑计算线程,第七种和第八种就是相应的在其基础上做了改进,增加线程池方案。

reactor_threadpool

第九种也就是很多人所提倡的one poll per thread.

在这里都可以看到,除了第九种IO的处理都只是在main thread上完成的,IO处理完成后把任务交给thread,而不是让thread处理IO.

这样做的好处就是work thread只负责处理逻辑,main thread只负责处理IO,完全的分开了两者的任务. 如作者所说,如果计算任务是彼此独立的,IO压力不大的情况下,那么这种方案能带来不错的效果。

第九种也就是现在使用最多的一种,(Nginx采用多进程).

reactor_multiple

主进程负责accept连接,再把连接fd挂到sub reactor(子进程或线程中),然后sub reactor负责后面的IO处理和计算,如果需要优化防止突发请求过高,在 sub reactor中再使用thread处理IO.

 

posted @ 2013-05-01 13:08  _Boz  阅读(557)  评论(2编辑  收藏  举报
4AI?Z:cp1z?_RJQle1]Gs;P!T)RHroW|