浅谈Nginx服务器的内部核心架构设计

前言

Nginx 是一个 免费的开源的高性能HTTP 服务器和 反向代理,以及 IMAP/POP3 代理服务器。 Nginx 以其高性能,稳定性,丰富的功能,简单的配置和低资源消耗而闻名。Nginx是一个 Web 服务器,也可以用作 反向代理负载均衡器HTTP 缓存

很多高知名度的网站都使用 Nginx,如:NetflixGitHubSoundCloudMaxCDN 等。

正文

1. Nginx的整体架构

1.1. 主进程

Nginx 启动时,会生成两种类型的 进程*,一个是 主进程master),一个windows 版本的目前只有一个)或 多个工作进程worker)。主进程 并不处理网络请求,主要负责 调度工作进程,也就是图示的 3 项:加载配置启动工作进程非停升级。所以,Nginx 启动以后,查看操作系统的进程列表,我们就能看到 至少有两个 Nginx 进程。

1.2. 工作进程

服务器实际 处理网络请求响应 的是 工作进程worker),在类 unix 系统上,Nginx 可以配置 多个 worker,而每个 worker 进程 都可以同时处理 数以千计网络请求

1.3. 模块化设计

Nginxworker 进程,包括 核心功能性模块核心模块 负责维持一个 运行循环run-loop),执行网络请求处理的 不同阶段 的模块功能,比如:网络读写存储读写内容传输外出过滤,以及 将请求发往上游服务器 等。而其代码的 模块化设计,也使得我们可以根据需要对 功能模块 进行适当的 选择修改,编译成具有 特定功能 的服务器。

1.4. 事件驱动模型

基于 异步及非阻塞事件驱动模型,可以说是 Nginx 得以获得 高并发高性能 的关键因素,同时也得益于对 LinuxSolaris 及类 BSD 等操作系统内核中 事件通知I/O 性能增强功能 的采用,如 kqueueepollevent ports

1.5. 代理(proxy)设计

代理设计,可以说是 Nginx 深入骨髓的设计,无论是对于 HTTP,还是对于 FastCGIMemcacheRedis 等的网络请求或响应,本质上都采用了 代理机制。所以,Nginx 天生就是高性能的 代理服务器

2. Nginx的模块化设计

高度模块化 的设计是 Nginx 的架构基础。Nginx 服务器被分解为 多个模块,每个模块就是一个 功能模块,只负责自身的功能,模块之间严格遵循 “高内聚,低耦合” 的原则。

2.1. 核心模块

核心模块Nginx 服务器正常运行 必不可少 的模块,提供 错误日志记录配置文件解析事件驱动机制进程管理 等核心功能。

2.2. 标准HTTP模块

标准 HTTP 模块提供 HTTP 协议解析相关的功能,比如:端口配置网页编码设置HTTP 响应头设置 等等。

2.3. 可选HTTP模块

可选 HTTP 模块主要用于 扩展 标准的 HTTP 功能,让 Nginx 能处理一些特殊的服务,比如:Flash 多媒体传输、解析 GeoIP 请求、网络传输压缩安全协议 SSL 支持等。

2.4. 邮件服务模块

邮件服务模块 主要用于支持 Nginx邮件服务,包括对 POP3 协议、IMAP 协议和 SMTP 协议的支持。

2.5. 第三方模块

第三方模块 是为了扩展 Nginx 服务器应用,完成开发者自定义功能,比如:Json 支持、Lua 支持等。

3. Nginx的请求方式处理

Nginx 是一个 高性能Web 服务器,能够同时处理 大量的并发请求。它结合 多进程机制异步机制,异步机制使用的是 异步非阻塞方式,接下来就给大家介绍一下 Nginx多线程机制异步非阻塞机制

3.1. 多进程机制

服务器每当收到一个客户端时,就有 服务器主进程master process)生成一个 子进程worker process)出来和客户端建立连接进行交互,直到连接断开,该子进程就结束了。

使用 进程 的好处是 各个进程之间相互独立不需要加锁,减少了使用锁对性能造成影响,同时降低编程的复杂度,降低开发成本。其次,采用独立的进程,可以让 进程互相之间不会影响,如果一个进程发生异常退出时,其它进程正常工作,master 进程则很快启动新的 worker 进程,确保服务不会中断,从而将风险降到最低。

缺点是操作系统生成一个 子进程 需要进行 内存复制 等操作,在 资源时间 上会产生一定的开销。当有 大量请求 时,会导致 系统性能下降

3.2. 异步非阻塞机制

每个 工作进程 使用 异步非阻塞方式,可以处理 多个客户端请求

当某个 工作进程 接收到客户端的请求以后,调用 IO 进行处理,如果不能立即得到结果,就去 处理其他请求(即为 非阻塞);而 客户端 在此期间也 无需等待响应,可以去处理其他事情(即为 异步)。

IO 返回时,就会通知此 工作进程;该进程得到通知,暂时 挂起 当前处理的事务去 响应客户端请求

4. Nginx事件驱动模型

Nginx异步非阻塞机制 中,工作进程 在调用 IO 后,就去处理其他的请求,当 IO 调用返回后,会 通知工作进程。对于这样的系统调用,主要使用 Nginx 服务器的 事件驱动模型 来实现。

如上图所示,Nginx事件驱动模型事件收集器事件发送器事件处理器 三部分基本单元组成。

  • 事件收集器:负责收集 worker 进程的各种 IO 请求;

  • 事件发送器:负责将 IO 事件发送到 事件处理器

  • 事件处理器:负责各种事件的 响应工作

事件发送器 将每个请求放入一个 待处理事件列表,使用非阻塞 I/O 方式调用 事件处理器 来处理该请求。其处理方式称为 “多路 IO 复用方法”,常见的包括以下三种:select 模型、poll 模型、epoll 模型。

5. Nginx进程处理模型

Nginx 服务器使用 master/worker 多进程模式。多线程启动和执行的流程如下:

  1. 主程序 Master process 启动后,通过一个 for 循环来 接收处理外部信号

  2. 主进程 通过 fork() 函数产生 worker 子进程,每个 子进程 执行一个 for 循环来实现 Nginx 服务器 对事件的接收处理

一般推荐 worker 进程数CPU 内核数 一致,这样一来不存在 大量的子进程 生成和管理任务,避免了进程之间 竞争 CPU 资源进程切换 的开销。而且 Nginx 为了更好的利用 多核特性,提供了 CPU 亲缘性 的绑定选项,我们可以将某 一个进程绑定在某一个核 上,这样就不会因为 进程的切换 带来 Cache 的失效。

对于每个请求,有且只有一个 工作进程 对其处理。首先,每个 worker 进程都是从 master 进程 fork 过来。在 master 进程里面,先建立好需要 listensocket(listenfd) 之后,然后再 fork 出多个 worker 进程。

所有 worker 进程的 listenfd 会在 新连接 到来时变得 可读,为保证只有一个进程处理该连接,所有 worker 进程在注册 listenfd 读事件抢占 accept_mutex,抢到 互斥锁 的那个进程 注册 listenfd 读事件,在 读事件 里调用 accept 接受该连接。

当一个 worker 进程在 accept 这个连接之后,就开始 读取请求解析请求处理请求,产生数据后,再 返回给客户端,最后才 断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由 worker 进程来处理,而且只在一个 worker 进程中处理。

Nginx 服务器的运行过程中,主进程工作进程 需要进程交互。交互依赖于 Socket 实现的 管道 来实现。

5.1. 主进程与工作进程交互

这条管道与普通的管道不同,它是由 主进程 指向 工作进程单向管道,包含主进程向工作进程发出的 指令工作进程 ID 等;同时 主进程 与外界通过 信号通信;每个 子进程 具备 接收信号,并处理相应的事件的能力。

5.2. 工作进程与工作进程交互

这种交互是和 主进程-工作进程 交互是基本一致的,但是会通过 主进程 间接完成。工作进程 之间是 相互隔离 的,所以当工作进程 W1 需要向工作进程 W2 发指令时,首先找到 W2进程 ID,然后将正确的指令写入指向 W2通道W2 收到信号采取相应的措施。

小结

通过这篇文章,我们对 Nginx 服务器的 整体架构 有了一个整体的认识。包括其 模块化的设计多进程异步非阻塞 的请求处理方式、事件驱动模型 等。通过这些理论知识,才能更好地领悟 Nginx 的设计思想。对于我们学习 Nginx 来说有很大的帮助。


欢迎关注技术公众号: 零壹技术栈

零壹技术栈

本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

文章作者: Chen Vainlgory
文章链接: https://geek.vainlgory.top/2018/07/15/浅谈Nginx服务器的内部核心架构设计/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 零壹技術棧 | VainlgoryのBlog
微信打赏
支付宝打赏