单个服务器如何处理请求
web服务器最简单的形式就是一个程序,它侦听HTTP请求,在收到一个HTTP请求之后做出回复。当然在接收请求后服务器所做的东西是我们关注的焦点。在下文中也会提及到node是如何简单的实现一个web服务器。
常用的是Apache,tomcat等服务器解决web请求。现在更多的人会使用Nginx解决web请求,这也有一定的道理。本文主要研究各服务器比较成熟的多任务处理方式。下文会详细的提到。
Apache:
说到web应用平台,基本上第一反应都是LAMP这个经典的平台。其中Apache使用的时间也是众多服务器中使用比较稳定的。对于单个请求,Apache处理的流程和基本的web服务器流程差不多,我们主要看一下Apache的多任务处理:
(1) 我们知道操作系统早就实现了并发机制,则充分利用操作系统的并发机制提高服务器的单机运行效率是服务器开发者不断追求的目标。但是,因为不同的操作系统平台和计算机硬件的差别使得并发模型没有一个通用的规则,在很多平台上非常优秀的并发模型换到另一个平台上就变成性能很差;另外同一个系统平台但是执行的任务不同,同一个并发模型的并发性能也有很大差异;所以并发的性能的影响因素包括:
- 操作系统平台
- 服务器跑的任务
(2) Apache 是用C语言开发的,所以入口是一个main主程序,主程序一旦调用MPM模型中的ap_mpm_run函数后,就进入了多进程并发处理状态,为了并发处理客户端的请求,Apache会产生多个进程,而每个进程又会产生多个线程.
Aapche提供了多个并发模型:
- prefork:一般用于Unix操作系统,基于进程的并发,以进程为一个处理client请求的基本单位;有多个子进程(但没有线程);一般Linux系统会默认次并发模型
- worker:新的MPM,同事使用了线程和进程,效率比单纯使用进程的Prefork MPM要高(既有进程、也有线程)
- WinNT :适用于windows系统平台,以线程作为处理client请求的基本单位
(3) MPM的主要任务
MPM的主要任务是创建进程或线程并对它们进行管理,包括在空闲的时候终止多余的进程或线程,以及在忙碌的时候启动更多的进程和线程;另外一个任务是在套接字上(socket)上监听客户端的请求,当请求到达时,MPM将把它派发给创建好的进程和线程,继而进入请求处理阶段(每一个进程或线程将自始至终只负责一个客户端的请求)
其实这里就包含了很多我们需要慢慢梳理的东西。比如Apache的进程模型:Apache里面多进程模式(多线程模式)。都是Apache本身维护的一个进程池来分配进程来处理单个请求的。进程和线程的优缺点这里也不一一述说。
Nginx篇
在说Nginx之前,我想说一下Node中一个比较好的处理请求的方式,采用异步I/O的方式。就像我们编程一样,单线程同步编程顺序执行任务的方式比较符合编程人员顺序思考的思维方式,但是会因阻塞I/O导致硬件资源得不到更优的使用。而多线程编程模型也因为编程中的死锁、状态同步等问题而头疼。
首先我们来了解阻塞和非阻塞两个名词(简单描述,具体的可以阅读操作系统方面的知识):
阻塞I/O的一个特点就是调用之后一定要等到系统内核层面完成所有操作后,调用才结束。阻塞I/O最主要的就是等待过程中有可能浪费时间。不能使CPU的处理能力最大化
非阻塞I/O和阻塞的区别在于非阻塞I/O不带数据直接返回,等要获取数据,还需要通过文件描述符再次读取。非阻塞I/O返回之后,CPU的时间片可以用来处理其他事务。但非阻塞I/O也存在一些问题。由于完整的I/O没有完成,立即返回的并不是业务层期望的数据,而仅仅是当前调用的状态。为了获取完整的数据,应用程序需要重复调用I/O操作来确认是否完成。这种重复调用判断操作是否完成的技术叫做轮询。
轮询技术(主要):
select/poll:创建一个关注事件的描述符集合,再去等待这些事件发生,然后再轮询描述符集合,检查事件发生,有就进行处理
epoll:把描述符列表的管理交由内核负责,一旦有事件发生,内核把发生事件的描诉符列表通知给进程,避免了轮询整个描述符列表。
rtsig:工作进程会通过系统内核建立一个rtsig队列用于存放标记事件发生的信号。
上述描述整理出完成整个异步I/O的环节为:事件循环(典型的生产者/消费者),观察者,请求对象,执行回调。
则Node通过事件驱动的方式处理请求,无须每一个请求创建额外的对应线程,可以省掉创建线程和销毁线程的开销,生成的线程少,减低上下文切换。
插入图片
其实Nginx内部实现和Node有异曲同工之妙。下面说一下Nginx服务器的web请求处理机制:
1.多进程方式:多进程方式的优点在于各个子进程之间相互独立,处理客户端请求的过程彼此不受到干扰,不会相互影响,提供服务的稳定性。缺点也很明显:操作系统生成一个子进程需要进行内存复制等操作,在资源和事件产生额外开销。(apache可以采用预生成进程机制改进,交互完成后,该进程也不结束)
2.多线程方式: 缺点:多个线程位于同一个进程内,可以访问同样的内存空间,彼此之间相互影响。都要由开发者对内存进行管理
3.异步方式
同步机制:指发送方发送请求后,需要等待接受到接收方发回的响应后,才接着发送下一个请求;
异步机制:发送方发送一个请求后,不等待接收方相应这个请求,就继续发送下一个请求。形成一个队列,接收方处理完成后通知发送方。
阻塞调用方式为,调用结果返回之前,当前线程从运行状态被挂起,一直等到结果返回之后,才进入就绪状态,获取cpu后继续执行
非阻塞调用:线程不被挂起,立即返回执行下一个调用。
Nginx服务器有一个master进程,多个worker进程,工作进程使用了异步非阻塞方式。
Nginx的事件处理机制(IO状态通知给工作进程,IO调用完成后能主动通知工作进程,事件驱动模型)
事件驱动模型:事件收集器,事件发送器,事件处理器(多路IO复用方式)参考轮询的实现。
当然Nginx有其他很多的功能:比如作为反向代理缓存和负载均衡方面也有比较好的表现。下几篇就会介绍。
至于Tomcat还在学习中,具体的处理模型也会和Apache等进行比较。希望尽快能发文。
小结:本篇主要学习了服务器多任务处理请求的流程。也有很多不太清楚的地方。请大家多多指点。