Nginx的进程模型详解:高性能架构的奥秘
Nginx的进程模型详解:高性能架构的奥秘
进程模型
详述版
Nginx 在启动后,在 Unix 系统中会以 Daemon 的方式在后台运行,后台进程包含一个 Master 进程和多个 Worker 进程。
Master 进程主要用来管理 Worker 进程,包含:接收来自外界的信号,向各 Worker 进程发送信号,监控 Worker 进程的运行状态,当 Worker 进程异常退出后,会自动重新启动新的 Worker 进程。
而基本的网络事件,则是放在 Worker 进程中来处理了。多个 Worker 进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个 Worker 进程中处理,一个 Worker 进程,不可能处理其它进程的请求。Worker 进程的个数是可以设置的,一般我们会设置与机器 CPU 核数一致,这里面的原因与 Nginx 的进程模型以及事件处理模型是分不开的。
Nginx 的进程模型,可以由下图来表示:
精简版
- 主从架构:
- Master 进程:负责管理 Worker 进程,包括接收信号、启动或终止 Worker 进程、监控其状态。
- Worker 进程:独立处理客户端请求,每个请求仅由一个 Worker 处理。
- 多 Worker 设计:
- Worker 进程互相独立,平等竞争处理请求。
- Worker 数量通常与 CPU 核心数相等,充分利用多核能力。
- 通过抢占机制(accept_mutex)确保同一连接只被一个 Worker 处理。
进程控制
详述版
在 Nginx 启动后,如果我们要操作 Nginx,要怎么做呢?从上文中我们可以看到,Master 来管理 Worker 进程,所以我们只需要与 Master 进程通信就行了。Master 进程会接收来自外界发来的信号,再根据信号做不同的事情。所以我们要控制 Nginx ,只需要通过 kill 向 Master 进程发送信号就行了。比如
kill -HUP pid
,则是告诉 Nginx ,从容地重启 Nginx ,我们一般用这个信号来重启 Nginx ,或重新加载配置,因为是从容地重启,因此服务是不中断的。
Master 进程在接收到 HUP 信号后是怎么做的呢?
- 首先 Master 进程在接到信号后,会先重新加载配置文件,然后再启动新的 Worker 进程,并向所有老的 Worker 进程发送信号,告诉他们可以光荣退休了。
- 新的 Worker 在启动后,就开始接收新的请求,而老的 Worker 在收到来自 Master 的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。
当然,直接给 Master 进程发送信号,这是比较老的操作方式,Nginx 在 0.8 版本之后,引入了一系列命令行参数,来方便我们管理。比如
./nginx -s reload
,就是来重启 Nginx ,
./nginx -s stop
,就是来停止 Nginx 的运行。
如何做到的呢?我们还是拿 reload 来说,我们看到,执行命令时,我们是启动一个新的 Nginx 进程,而新的 Nginx 进程在解析到 reload 参数后,就知道我们的目的是控制 Nginx 来重新加载配置文件了,它会向 Master 进程发送信号,然后接下来的动作,就和我们直接向 Master 进程发送信号一样了。
现在,我们知道了当我们在操作 Nginx 的时候, Nginx 内部做了些什么事情,那么,Worker 进程又是如何处理请求的呢?我们前面有提到,Worker 进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供 80 端口的 HTTP 服务时,一个连接请求过来,每个进程都有可能处理这个连接,
怎么做到的呢?首先,每个 Worker 进程都是从 Master 进程 Fork 过来,在 Master 进程里面,先建立好需要 Listen 的 Socket(listenfd)之后,然后再 Fork 出多个 Worker 进程。所有 Worker 进程的 listenfd 会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有 Worker 进程在注册 listenfd 读事件前抢夺 accept_mutex,抢到互斥锁的那个进程注册 listenfd 读事件,在读事件里调用 accept 接受该连接。当一个 Worker 进程在 accept 这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由 Worker 进程来处理,而且只在一个 Worker 进程中处理。
精简版
进程控制
- 信号控制:
- 通过 kill 命令向 Master 进程发送信号控制 Nginx:
kill -HUP pid
:从容重启(重新加载配置)。
kill -QUIT pid
:优雅关闭。
- 命令行参数:
- 从 Nginx 0.8 起,支持更便捷的命令操作:
nginx -s reload
:重启并加载新配置。
nginx -s stop
:立即停止服务。
- 本质上是新启动一个 Nginx 进程,通过参数向 Master 进程发送信号完成控制。
请求处理流程
- Master 初始化:监听所需的 Socket(listenfd)。
- Worker 创建:通过 Fork 共享监听 Socket。
- 连接竞争:
- 新连接到来时,所有 Worker 的 listenfd 均变为可读。
- 通过 accept_mutex 保证仅一个 Worker 处理该连接。
- 请求处理:
- Worker 负责从接收请求、解析、处理到响应的全过程。
进程模型的好处
详述版
那么, Nginx 采用这种进程模型有什么好处呢?当然,好处肯定会很多了。首先,对于每个 Worker 进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,Master 进程则很快启动新的 Worker 进程。当然,Worker 进程的异常退出,肯定是程序有 Bug 了,异常退出,会导致当前 Worker 上的所有请求失败,不过不会影响到所有请求,所以降低了风险。当然,好处还有很多,大家可以慢慢体会。
精简版
Nginx 的主从架构结合多 Worker 模型,在高性能和高可靠性间取得了很好的平衡,适合高并发场景下的高效网络服务处理。
优势
- 独立性:Worker 进程独立运行,避免锁机制,简化编程并减少锁带来的性能开销。
- 稳定性:单个 Worker 异常退出不影响整体服务,Master 能迅速启动新 Worker 维持服务稳定。
- 高效性:充分利用多核优势,提升并发处理能力。