浅谈服务器单I/O线程+工作者线程池模型架构及实现要点

jopen 8年前

这种架构主要是基于I/O多路复用的思想(主要是epoll,select/poll已过时),通过单线程I/O多路复用,可以达到高效并发,同时避免了多线程I/O来回切换的各种开销,思路清晰,易于管理,而基于线程池的多工作者线程,又可以充分发挥和利用多线程的优势,利用线程池,进一步提高资源复用性和避免产生过多线程。

1.模型架构

 

2 实现要点

2.1 单I/O 线程epoll

实现单I/O线程的epoll模型是本架构的第一个技术要点,主要思想如下:

单线程创建epoll并等待,有I/O请求(socket)到达时,将其加入epoll并从线程池中取一个空闲工作者线程,将实际的任务交由工作者线程处理。

伪码:

创建一个epoll实例;  while(server running)  {      epoll等待事件;      if(新连接到达且是有效连接)      {          accept此连接;          将此连接设置为non-blocking;          为此连接设置event(EPOLLIN | EPOLLET ...);          将此连接加入epoll监听队列;          从线程池取一个空闲工作者线程并处理此连接;      }      else if(读请求)      {          从线程池取一个空闲工作者线程并处理读请求;      }      else if(写请求)      {          从线程池取一个空闲工作者线程并处理写请求;      }      else          其他事件;       }

伪码可能写的不太好,其实就是基本的epoll使用。

但要注意和线程池的配合使用,如果线程池取不到空闲的工作者线程,还需要做一些处理。

2.2 线程池实现要点

server启动时,创建一定数量的工作者线程加入线程池,如(20个),供I/O线程来取用;

每当I/O线程请求空闲工作者线程时,从池中取出一个空闲工作者线程,处理相应请求;

当请求处理完毕,关闭相应I/O连接时,回收相应线程并放回线程池中供下次使用;

若请求空闲工作者线程池时,没有空闲工作者线程,可作如下处理:

(1)若池中”管理”的线程总数不超过最大允许值,可创建一批新的工作者线程加入池中,并返回其中一个供I/O线程使用;

(2)若池中”管理”的线程总数已经达到最大值,不应再继续创建新线程, 则等待一小段时间并重试。注意因为I/O线程是单线程且不应被阻塞等待在此处,所以其实对线程池的管理应由一个专门的管理线程完成,包括创建新工作者线程等工作。此时管理线程阻塞等待(如使用条件变量并等待唤醒),一小段时间之后,线程池中应有空闲工作者线程可使用。否则server负荷估计是出了问题。