从半同步-半异步模式谈服务器的设计 - 那谁的技术博客 - C++博客
从半同步-半异步模式谈服务器的设计
半同步-半异步模式,最早应该是由ACE的作者提出,原文在 这里.
简而言之,所谓的半同步半异步模式分为三个组成模块:同步处理模块,队列模块,异步处理模块.三个模块之间的交互关系如图:
![]()
(注:上图出自 这里)
几个模块的之间的交互为:异步模块接收可能会异步到来的各种事件(I/O,信号等),然后将它们放入队列中,而同步模块一般只有一种动作,就是不停的从队列中取出消息进行处理.
半同步-半异步模式的出现是为了给服务器的功能进行划分,尽可能将的可能阻塞的操作放在同步模块中,这样不会影响到异步模块的处理.
举个例子说明.
假设现在有一个服务器,在接收完客户端请求之后会去数据库查询,这个查询可能会很慢.这时,如果还是采用的把接收客户端的连接和处理客户端的请求(在这里这个处理就是查询数据库)放在一个模块中来处理,很可能将会有很多连接的处理响应非常慢.
此时,考虑使用半同步半异步的模式,开一个进程,使用多路复用IO(如epoll/select)等监听客户端的连接,接收到新的连接请求之后就将这些请求存放到通过某种IPC方式实现的消息队列中,同时,还有N个处理进程,它们所做的工作就是不停的从消息队列中取出消息进行处理.这样的划分,将接收客户端请求和处理客户端请求划分为不同的模块,相互之间的通过IPC进行通讯,将对彼此功能的影响限制到最小.
然后,不是每种请求下都适合使用半同步半异步模式的.
我之前深入阅读过ligty的代码,它的设计是monitor+worker多进程 + 多路复用IO + 状态机的架构.也就是说,每个worker进程负责接收客户端连接和处理客户端连接的全过程,每个过程都会记录一个状态,比如现在在接收包头,如果这次的接收不是因为连接关闭的原因导致的接收错误,那么就将这个客户端的fd放入多路复用IO中,等待着下一次根据这次保存的状态进入状态机中进行处理.
简单的说,在ligty中,一个worker子进程全权负责了接收和处理的全过程,并没有按照上面半同步半异步的划分来设计.
再后来,我大概看过一些nginx的代码,细节之处可能不一样,但是就服务器总体的架构而言,是与ligty的设计差不多的.
这两个服务器是目前比较快的web服务器了,没有采用多么复杂的模式.
那么为什么对于web服务器而言,不需要使用半同步半异步也可以达到非常高的效率呢?我想,这与服务器的业务有关.对于web服务器而言,大部分的时间都花在了IO处理上,比如监听服务器端口,接收客户端连接,根据客户端的请求发送文本文件内容到客户端去,这里的操作,基本上没有太可能会造成阻塞的地方,也就是说,处理完成一个客户端请求的全过程对web服务器而言是非常快的.
所以,要回答这个问题,需要看具体的业务需求.打个比方,如果处理一个客户端请求需要10s,那么完全有一个模块全部处理不是一个很好的设计;反之,如果处理一个请求只需要10ms,而进程/线程间的切换就需要1s了,还将模块进行划分就不必了.
另外,回到半同步半异步模式的具体实现上,可以使用线程或者进程,而队列层则可以使用不同的IPC方式,有很多关于多线程多进程孰优孰劣的争论,由于我没有太多多线程的编程经验,也就不在这里进一步说明了.