网络服务器如何处理并发请求的模型称之为多任务体系结构。
1.Inetd模式:在大部分Unix操作系统中,默认的多任务体系结构是Inetd应用程序。通用的网络服务器体系结构Inetd分为两个部分:主服务进程和客户服务进程。主服务进程通常用于等待客户端的连接请求。一旦客户端发起一个请求,主服务器将建立连接,同时调用fork创建一个新的客户服务进程,并由客户服务进程处理客户端的请求,而主服务进程继续返回进入等待状态,等待客户端的请求。
Inetd体系结构适合于两种情况:第一,处理客户端请求需要大量的时间才能完成,比如FTP,一般情况下,对于稍微大一些的文件FTP可能需要几秒甚至几分钟的时间。第二,如果子进程需要记住通信的会话状态,那么Inetd模式也是适用的,这样,在第一次返回响应之后,客户端和服务端的连接就不需要终止。
但是对于Http请求而言,上面的两点都变成缺点。首先Http是无状态的协议,因此在每次会话结束后,服务器都不存储上一次的状态,他只对当前的请求进行响应。因此如果Http服务器基于Inetd体系结构,其效率将是非常的低。另外,web服务器属于高并发、轻负载的服务类型,这就意味着,服务器每次处理时间都非常短,但在短的时间内需要处理连接会非常多。而Inetd采用的是进程创建的形式,不论Unix还是Linux上都是非常耗时的,因此不能满足高并发的需求。
2.Leader/Follower模式:通常情况下,服务器中的进程采用的都是即时创建的策略,一旦一个新的客户请求就立即创立一个新的进程或线程,而当进程或线程运行完毕,进程或线程也随之退出。显然,这种策略对于小规模的服务器还能接受,但对于大规模的服务器而言,创建进程或线程的时间将增加,最终会导致响应时间变长,单位时间内请求处理效率降低。L/F模式则不同,它首先一次性创建多个进程或线程,保存到系统中,这些进程或线程担任三种不同的角色:侦听者、工作者和空闲者,其含义分别如下:
(1)侦听者角色,该线程负责侦听客户端的请求,在L/F模式中它属于Leader的角色。通常情况下只运行一个进程或线程担当侦听者的角色。
(2)工作者角色,侦听者侦听到客户端的请求时,它将立即转为工作者角色并开始处理客户端的请求。工作者的线程可以有多个。
(3)空闲者角色,工作者执行任务完毕后它不立即退出,而是转变它的角色为空闲者,并呆在空闲者队列中。空闲者出现的原因是客户端请求不够多。空闲者们等待变为侦听者。而当侦听者变为工作者后,空闲者中的每一个都相互竞争,最终将会有一个线程变为侦听者,其余的继续保持空闲者的状态。
(4)几个可能出现的极端情况是:所有线程都变为工作者,忙于处理客户端的请求,没有线程担任侦听者的角色,因此此时客户端的请求都被拒绝;另一个极端就是没有工作者,如果没有任何请求到达,那么所有的线程都处于空闲状态。
以上介绍的两种模型是非常经典的也是被广泛使用的架构,下面介绍三种在Apache服务器中所使用的三种模型。
3.Prefork:基于进程的并发模型。在该模型中存在一个主进程和多个子进程。每一个子进程都会为所进行的请求侦听一个套接字。当接受到请求时,子进程就会接受它并且提供响应。父进程会监听所有子进程以确保总是可以使用最少量的进程来处理请求,并且确保等候请求到达的空闲进程不能太少。如果没有足够多的空闲进程来处理潜在的请求高峰,那么父进程就会启动新的子进程。如果存在过多的子进程,那么父进程会每次终止一个空闲进程,直到服务器回到最大空闲子进程状况。通过保持一定数量的空闲子进程来接受所引入的请求,服务器就可以避免在接受到请求时再去启动新进程的开销。
父进程和子进程之间通过记分板(一块共享内存区域)进行通信,对于每一个产生的子进程,它的状态信息都写入到记分板中,父进程通过读取记分板可以了解子进程的状态。当需要关闭子进程时它将通过终止管道发送终止信息给子进程,另外的一种通知方式就是通过信号。
4.worker模型:Prefork是一个精心设计的并发模式,不过因基于进程的先天的限制,在一些操作系统中使用并发的效率并不是很高。在一些系统中,比如Linux,线程是更适合的执行单元,与进程相比,它能够节省更多的资源及各种上下文切换。Worker是混合了进程和线程的并发模型。整个worker的内部可以分为三大部分:主进程、工作子进程及工作线程。主进程启动后,它会建立一组数目不定的工作子进程,子进程的数目由主进程进行动态调整,这与Prefork非常相似,每个子进程又会建立固定数目的工作子线程。
每个子进程产生的线程属于一组,每组中的线程分为两种角色:侦听者线程和工作者线程。侦听者线程用于侦听网络并接受客户端连接,一旦接受完毕,就将会放入连接队列中。然后,工作者线程负责从队列中获取连接,为所有来自它的请求提供服务。侦听者线程和工作者线程之间通过套接字队列进行异步通信。
当服务器繁忙时,通常需要产生更多的工作者线程。但是Worker并不直接创建线程,它首先创建一个子进程,然后有这个进程一次性创建多个线程。因此Worker中线程的创建总是批量的,与之类似,当服务器空闲时,Worker也不是逐个的终止某个特定线程,它仍然以进程为单位,终止一个进程,让后该进程一次性的终止该进程下的所有线程。
该模型可参考:并发模型(二)——Master-Worker模式
5.WinNT:对于Apache而言,WinNT是windows平台下唯一使用的MPM。与通常的MPM结果相比,WinNT具有一些明显的优势。首先它使用线程而不是进程作为基本的执行单元。另外,它运行的线程数目是固定不变的,这与前面所描述的Prefork的动态调整形成了对比。在windows下使用线程的最主要的原因是因为线程能够得到更高的性能提升。与Unix进程相比,windows进程实在是重量级单位,它消耗的资源要比线程多得多。
整个WinNT内部可以分为三个重要的组成部分:两个进程及一组多个工作线程。两个重要的进程分别是:监控进程和工作进程。当MPM启动后它会立即创建一个新的工作进程,而原有的进程则转为监控该工作进程。工作进程则负责产生多个线程并监控这些线程。所有的工作线程都由工作进程即新进程负责创建。工作进程负责处理客户端的HTTP请求。监控进程又被称为主进程,它负责监控工作进程的状态,确保工作进程正常工作。任何时候发现工作进程异常退出,它将重新启动该进程。而在工作线程内部各个线程又被分为不同的角色:侦听线程和工作线程。侦听线程负责接受客户端的连接请求,而工作线程则主要负责处理这些请求。侦听线程和工作线程之间是异步的,它们会通过“套接字队列”进行通信。
附:为了做到高并发的处理,windows和Linux系统对IO分别采用了不同的机制,在Linux系统中采用的是epoll而在windows系统下采用的是完成端口。
在服务器并发模型中,往往会涉及到线程池,关于线程池可参考:线程池的原理及实现