模块化
模块化是指:
- 以功能块为单位进行程序设计,一个模块就是一个功能块,应该只负责一个功能,遵循“单一职责原则”的设计模式。
- 自顶向下分解、逐步求精原则
- 模块内部高内聚、模块之间的依赖关系低耦合原则。
模块化设计支持分布式开发,可以使得nginx灵活的扩展和便捷的升级。
Nginx模块化结构
分为:
- 核心模块,指源码包中的模块,src文件夹下,包括进程管理、权限控制、错误日志记录等。
- 标准http模块,编译后包含的模块,使得nginx支持基本http功能
- 可选http模块,用于扩展标准的http功能,使得nginx能处理一些特殊的http请求
- 邮件服务模块,用于支持nginx的邮件服务,编译时默认不包含,需要通过with_mail_models等选项来指定
- 第三方模块
nginx服务器的web请求处理机制
一些基本概念
异步、同步
同步和异步,这两个概念是从客户端与服务器之间的通信交互方式得来的。
同步,指客户端向服务器发送一个请求后、必须接收到服务器对此请求的响应后,才会再向服务器发送下一个请求。
异步,指客户端向服务器发送一个请求后,并不等待服务器对此请求的响应,便发送了下一个请求。
阻塞、非阻塞
阻塞和非租塞,这两个概念是从服务器内部处理请求的方式来说的。
阻塞,指服务器接收到请求后,如果遇到IO阻塞,当前线程会被挂起,知道IO完成后唤醒当前线程,当前线程期间不会去处理其他事情。
非租塞,指服务器接收到请求后,如果遇到IO阻塞,当前线程不会挂起,而是会立即返回去执行下一个调用。
组合
同步阻塞:
同步非租塞:
异步阻塞:
异步非阻塞:
常用的http请求处理机制
多进程
apache采用多进程机制,对于每个客户端连接请求,会有一个单独的进程去处理此连接请求。
apache的多进程机制采用预生成进程池的机制。稍微缓解了进程的生成时,资源和时间开销的问题,但是仍比较耗资源。
多线程
IIS服务器采用多线程机制。需要开发人员负责内存管理,还要注意多线程共享变量的问题,比较复杂,
异步方式
异步非租塞
nginx处理请求的机制
多进程+异步非租塞
- 预先生成多个工作进程,构成进程池。
- 每个工作进程采用异步非阻塞方式,可以处理多客户端的请求。
- 当某工作进程接收到客户端请求后,调用IO进行处理,如果不能立即得到结果,就去处理其它的请求;而客户端在此期间也无需等待响应,可以去处理其它的事情;当IO调用返回结果时,就会通知此工作进程;该进程得到通知,就会挂起当前处理的事务,将IO结果继续处理后响应给客户端。
非租塞的IO是如何把自己的状态通知给工作进程的?
两种方式:
- 工作进程按照一定频率不断的检查IO的状态,这种方式有另外的开销
- IO调用在完成后主动通知工作进程,无额外开销。
select / poll / epoll / kqueue 这样的系统调用就能支持第二种方式,这些系统调用也被称为事件驱动模型。他们提供了一种机制,让进程可以同时处理多个并发请求,不用关心IO调用的具体状态,IO调用完全由事件驱动模型来管理,事件准备好后就通知工作进程事件已经就绪。
事件驱动模型
事件驱动模型是一种古老的响应事件的模型,在公共关系、经济活动、计算机编程等领域均有广泛应用。
事件驱动是指在持续事务管理过程中,由当前时间点上出现的事件引发的调动可用资源执行相关任务,解决不断出现的问题,防止事务堆积的一种策略。
操作系统中事件驱动模型由事件收集器、事件发送器、事件处理器。
事件收集器专门负责收集所有事件,例如来自用户的(鼠标、键盘),来自硬件的(时钟事件),来自软件的(如操作系统、应用程序本身)。
事件发送器将时间收集器收集的事件分发给能够处理这些事件的目标对象(即事件处理器所处位置)。
事件处理器负责具体事件的响应工作,一般到实现阶段才完全确定。
如果一个系统是由事件驱动模型作为编程基础的,那么它的架构基本上是这样的:预先设计一个事件循环所形成的程序,这个时间循环程序构成了“事件收集器”,它不断检查目前要处理的时间信息,然后使用“事件发送器”传递给事件处理器。
windows操作系统中,“窗口”视图,通常是“目标对象”即事件处理器,当用户按键做输入时,窗口中的编辑框里要显示用户输入的字符。在此过程中,用户的“按键”事件被时间收集器收集到,然后被事件发送器发送给窗口,窗口就会处理“按键”事件,将用户输入显示到编辑框中。
Nginx事件驱动处理库:多路IO复用
select库
各个版本的linux和windows都支持的基本事件驱动模型库。
使用select的一般步骤:
- 创建所关注事件的描述符集合。对于每个描述符,可以关注其上的读(Read)事件、写(Write)事件、异常发生(Exception)事件,所以要创建三类描述符集合。分别用来收集读事件的描述符、写事件的描述符、异常发生事件的描述符。
- 调用底层提供的select()函数,等待事件发生。
- 轮询所有事件描述符集合中的每一个描述符,检查是否有相应的事件发生,然后处理事件。
nginx默认会在编译阶段将select库编译到基本http模块中去,可以使用–without-select_module来选择不编译该库。
poll库
仅linux支持,windows不支持。
其与select的基本实现方式相同,只不过创建关注的描述符集合时,不分成三个集合,而是一个集合包括所有描述符。
也会被默认编译进基本HTTP模块中。
epoll库
是poll库的一个变种,仅linux支持,效率比select高很多。
当描述符比较多时,遍历描述符集合、然后查找每个描述符是否有相应事件发生这一过程会效率较低。
epoll选择将描述符列表的管理交给内核复制,一旦有事件发生,内核会将有事件发生的描述符列表通知给进程,这样避免了应用程序轮询整个描述符列表。
epoll会通过相关调用,通知内核创建一个N个描述符的事件列表,然后给这些描述符设置所关注的事件,并把他添加到内核的事件列表中,在编码过程中也可以通过相关调用对事件列表中的描述符进行动态地删除和修改。
Nginx服务器架构
客户端
主进程 工作进程 服务器
缓存
主进程
- 读取、解析配置文件,验证其有效性
- 数据结构初始化
- 模块配置和注册
- 信号处理
- 网络监听生成
- 建立、绑定、关闭Socket
- 创建、管理、结束工作进程
- 接收指令,重启、升级、退出 服务器等
- 平滑重启,应用新配置
- 平滑升级,失败回滚
工作进程
- 接收客户端请求
- 将请求依次送入各模块进行过滤处理
- IO调用,获取响应数据
- 与后端服务器通信,接收后端服务器处理结果
- 数据缓存,访问缓存索引、查询和调用缓存
- 发送请求结果,响应客户端请求
- 接收主程序指令,重启、升级、退出等