- 1. 通用的多任务处理结构(Inetd)
网络服务器处理并发请求的模型叫做“多任务体系结构”,在Unix系统中默认的多任务体系结构是Inetd应用程序,(因为Inetd的一些缺点,所以Apache没有使用它)
Inetd分为两个部分:主服务进程和客户服务器进程;主服务器进程(Master Server)进程通常用于等待客户端的连接请求,一旦客户端发起一个请求,主服务进程将建立一个TCP连接,同时调用fork()创建一个新的客户服务进程,并由客户服务进程处理客户端的请求。
上图描述了Inetd多任务体系结构的工作原理:
(1) 一个或多个client想此台服务器发送了请求,首先此台服务器操作系统的TCP/IP通信服务接收到client的请求;因为Inetd主服务已经注册了此类请求由Inetd来处理,所以TCP/IP会把client的请求发送给Inetd主服务来处理请求;
(2) Inetd主服务收到TCP/IP通信服务转发的client请求后先与此client(多个client时按先后顺序第一个client)建立TCP连接,同时调用一个fork()创建一个进程,因为fork()创建进程相当于是对父进程(Inetd主服务进程)的克隆,所以继承了父进程的所有权限也能识别父进程与client建立TCP连接的socket,然后让新创建的子进程处理client的请求,知道client请求结束。
Inetd主进程的作用就是监听提供web服务的端口(80或443),当接收到client请求时先建立tcp连接然后分配给相映的子进程进行处理;
Socket中 accept()函数和select()函数的区别,server一直在响应的端口进行监听,一旦有来自client的请求利用accept()函数建立一个套接字连接;
select()函数是用来监听多个端口的状态的
Inetd适合与两种情况:
- client请求需要比较长的时间才能完成 比如:FTP 传输文件
- 子进程需要记住上次会话状态,这样第一次请求结束后不用断开连接,直接进行下一次处理
而我们说的web 服务器的Apache提供的服务,让上面两种情况正好变成了缺点;
- web服务器是高并发轻负载的服务器类型,所以一般client的请求只是一个网页很短的时间就结束了
- 因为HTTP协议是无状态的, 每一次TCP连接结束后不会记录上一次连接的状态
所以,Apache没有用通用的Inetd并发模型,而是采用 Prefork和worker两种并发模型。
2.Prefork MPM模型 (预派生 MPM)
(1) 处理client请求的进程或线程有三种角色:侦听者、工作者和空闲者
Unix平台上最新使用的MPM就是 Prefork MPM模型,也是Apache默认的并发模型;Prefork 是基于进程的并发模型,里面存在一个主进程和多个子进程,多个子进程会排队进行侦听客户端发送来的请求,
|--子进程n--.........--子进程k--子进程2--子进程1|----------->排队等待client发送来的请求,子进程1是侦听者,其他的是空闲着;当收到一个clietn请求时,子进程1会与client建立套接字连接并处理client请求,成为工作者;这时子进程2就变成了侦听者;依次这样循环。
上图描述了预派生MPM的工作原理:
- 在Linux启动后会启动Apache应用程序,此时会调用Prefork MPM模型,然后根据httpd.conf里面的 <prefork.c>的配置文件创建一个主进程和StartServers个子进程(也就是我们说的初始化,建立内存池,分配内存资源)
- Apache调用Prefork MPM模型后会把控制权交给MPM模型,预派生MPM会进入一个主循环(通过读计分板的信息监控子进程的状态,并控制空闲子进程的数量)
- 空闲子进程则排成队列等待client的请求,其实这也是一个循环:
空闲子进程------>等待client的请求------>建立连接并处理请求成为工作者------>处理完毕后断开连接重 新变成空闲子进程(这样一直循环处理client的请求)
- 当子进程与client建立套接字TCP连接后,其实也是一个循环的过程(MPM工作在 keep-alive状态),可能client会不断的发送请求,这样子进程会不断的处理请求返回数据,知道client请求完毕)
主进程的功能和任务:
- 在MPM住循环中接收来自外部关闭、重启、优雅启动的指令
- 在启动时创建一个新的主进程和一定数量的子进程
- 主进程还不断的到计分板上读取各个子进程的状态信息,然后根据当期并发的数量决定是关闭一部分空闲子进程还是创建一部分子进程
3. 工作者(worker) MPM
(1) worker MPM简介
prefork MPM 模型是 精心设计的但是因为其是基于进程的先天性限制,在一些操作系统中使用并发的效率不是很高;在很多操作系统中(比如Linux),线程是更适合执行的基本单元,因为线程能节省更多的资源(线程是轻量级的)和各种上下文切换(线程可以共享上下文);整个worker MPM 内部分为3大功能模块:主进程、工作子进程和工作线程;Aapche调用worker MPM后会根据httpd.conf内的配置文件创建一个主进程和 StartServers 个子进程,然后每个子进程会创建ThreadsPerChild个线程;所以worker MPM是一个进程和线程混合的并发模型;在改善了预派生MPM可扩展的同时获得可靠性和健壮性;
- 可扩展性:主进程可以根据并发数量控制子进程的数目,也可以控制线程的数目
- 可靠性、健壮性:如果一个线程崩溃时,只有输入同一个进程的线程会崩溃;其他子进程创建的线程仍有效的运行
(2) httpd.conf中 worker MPM的配置
<IfModule worker.c>
StartServers 3 ----->Apache调用workerMPM生成一个主进程后会自动生成3个子进程
MaxClient 2000 ----->Apache同时处理client的请求的最大值是2000;也就是并发处理量最大值是2000
ServerLimit 25 ----->主进程最多能创建25个子进程(创建多了会让MPM性能下降)
MinSpareThreads 50-->最小的空闲线程数量,如果低于50,主进程会创建一个子进程,子进程 会自动创建ThreadsPerChild个线程
MaxSpareThreads 200->最大的空闲线程数量,如果大于200,主进程会优雅关闭一个子进程里面的所有线程
ThreadLimit 200----->??
ThreadsPerChild 100-->每一个子进程创建的线程数目是固定的,100个
MaxRequestsPerChild 0-->0 代表不限制的意思
</IfModule>
上面的 MaxRequestsPerChild 10000 参数的意思是:如果一个线程累计处理的client请求总数达到10000,则主进程会关闭此线程所在的子进程(不确定???)
但是唉<IfModule prefork.c> 中
(3) worker 主进程MaxRequestsPerChild 10000 的意思就是如果一个子进程累计处理的client请求总数达到1000,则主进程会关闭此子进程,释放内存空间,如果需要则新生成一个子进程
worker MPM虽然是进程和线程混合的模型,但是基于线程来处理client的请求的,子进程指维护其创建的线程不会处理client请求;因为线程占用的系统资源开销比较少,所以比基于进程的预派生MPM处理并发数量大,并发能力强。
主进程的任务:
- 接受CPU指令开启、关闭、优雅重启apache
- 管理子进程的数目,以实现管理空闲线程的数目
子进程的任务:(子进程不处理client的请求)
- 启动apache时创建 ThreadsPerChild 个线程;其中一个是侦听线程和(ThreadsPerChild -1)个工作线程
- 作为一个中介传递主进程发给执行线程的消息,包括关闭、开启和优雅重启
(4) worker MPM 并发处理的基本原理:
预派生MPM是基于进程的并发,所有的子进程有三种角色:侦听者、工作者和空闲着,所有空闲子进程在一个队列中排队等候,轮流作为侦听者和工作者,处理连接完毕后变为空闲者继续排队
worker MPM 是基于线程的并发,在子进程创建固定的线程数目时就指定了其中一个是侦听者,其他的是工作者;其工作原理如下:
- 侦听线程会对各个端口(80/443)进侦听,如果有client请求发送过来,侦听线程会建立一个套接字并把套接字放在套接字连接队列中,然后侦听线程继续侦听,不做处理
- 工作线程会一直监听套接字连接队列,如果发现队列中有套接字为处理就会读取套接字然后与client建立TCP连接并处理client请求,处理完毕后工作线程还是工作线程还是继续监听套接字连接队列
(5) 主进程管理子进程和线程
主进程的一个重要任务就是管理线程的数量,当空闲的线程数量小于 MinSpareThreads 时,会创建一个子进程,这个子进程会创建固定数量的线程;主进程不能直接创建线程;同理当空闲的线程数量大于 MaxSpareThreads时,主进程会通过计分板和终止管道通知子进程然后子进程负责关闭这个子进程下面所有的线程;关闭一般次用优雅关闭,的那个线程处理完毕client请求后会自动判断现在的进程是否是创建它的进程如果不是会自动关闭。