服务端相关 / 20 Nginx 的基础架构解析(上)

Nginx 的基础架构解析(上)

1. Nginx 的进程模型

前面介绍 Nginx 时有介绍过 Nginx 的进程模型。Nginx 启动时首先启动一个 Master 进程,然后由 Master 进程启动一个或者多个 Worker 子进程。Master 进程主要完成配置读取,通过发送信号控制 Worker 进程的启动和停止等,而 Worker 子进程是用来处理客户端发来的 Http 请求,且Worker进程之间会通过共享内存进行通信。

1.1 worker 进程处理请求过程

假设 Nginx 启动了多个 Worker 进程,并且在 Master 进程中通过 socket 套接字监听80端口。
这些 Worker 进程 fork 自 Master 进程,然后每个worker进程都可以去 accept 这个监听的 socket。 当一个连接进来后,所有在 accept 在这个 socket 上面的进程,都会收到消息,但是只有一个进程可以 accept 这个连接,其它的则 accept 失败,这便是所谓的惊群现象。Nginx 处理这种情况的方式就是加锁。有了锁之后,在同一时刻,就只会有一个 Worker 进程在 accpet 连接,这样就不会有惊群问题了。在 Worker 进程拿到 Http 请求后,就开始按照前面介绍的 11个阶段处理该 Http 请求,最后返回响应结果并断开连接。

1.2 Nginx 命令行的处理流程

最早我们学习了 Nginx 命令行操作,这些命令行操作都是给 Master 进程发信号,然后再由 Master 进程发送信号给 Worker 进程,从而达到控制 Worker 进程的目标。我们以 Nginx 的热部署命令./nginx -s reload 来描述 Nginx 命令行的执行流程。具体过程如下:

  • 首先 Master 进程会检查 nginx.conf 文件是否存在语法错误,并从中找到 nginx.pid 配置路径(没有配置会使用默认值)

  • reload 参数表示向 Master 进程发送 HUP 信号。Nginx 会根据会保存在 nginx.pid 文件中的值找到 Master 进程的 pid。如果 Nginx 进程没有启动,则没有该 nginx.pid 文件,命令行会报错;

# 在 Nginx 的配置文件中配置 nginx.pid 的保存路径
[root@server sbin]# ./nginx -s reload
nginx: [error] open() "/root/nginx/logs/nginx.pid" failed (2: No such file or directory)
  • Master 进程打开新的监听端口;

  • Master 进程用新配置启动新的 Worker 进程。新的 Worker 进程起来后,开始接收 Http 请求并处理,此时老的 Worker进程会停止接受 Http 请求;

  • Master 进程会向老的 Worker进程发送 QUIT 信号;

  • 老的 Worker 进程关闭监听句柄,处理完正在进行的请求后结束进程。

Nginx 命令行中 -s 参数的每个值都对应这一个信号。因此,我们也可以直接对 Master 进程发生相应信号达到同样的目的。

# pid表示Nginx的主进程id号
# kill -s 信号 pid

2. Nginx的事件驱动模型

2.1 事件驱动模型

事件驱动模型是实现异步非阻塞的一个手段。对于 web 服务器来说,客户端 A 的请求连接到服务端时,服务端的某个进程(Nginx 的 worker 进程)会处理该请求,此进程在没有返回给客户端 A 结果时,它又去处理了客户端 B 的请求。服务端把客户端 A 以及客户端 B 发来的请求作为事件交给了"事件收集器",而"事件收集器"再把收集到的事件交由"事件发送器"发送给"事件处理器"进行处理。最后"事件处理器"处理完该事件后,通知服务端进程,服务端进程再把结果返回给客户端A、客户端B。在这个过程中,服务端进程做的事情属于用户级别的,而事件处理这部分工作属于内核级别的。也就是说这个事件驱动模型是需要操作系统内核来作为支撑的。

2.2 Nginx的事件驱动模型介绍

Nginx 的事件驱动模型,支持 select、poll、epoll、rtsig、kqueue、/dev/poll、eventport 等。
最常用的是前三种,特别是 epoll 模型,这也是 Nginx 中的默认配置。可以说 epoll 模型成就了 Nginx 的高性能和高并发特性。

  • select模型:
    • 创建所关注事件的描述符集合,对于一个描述符,可以关注其上面的读(Read)事件、写(Write)事件以及异常发生(Exception)事件。
      在select模型中,要创建这3类事件描述符集合。
    • 调用底层提供的select()函数,等待事件发生。
    • 轮询所有事件描述符集合中的每一个事件描述符,检查是否有相应的事件发生,如果有就进行处理。
  • poll模型:
    • poll模型是Linux平台上的事件驱动模型,在Linux2.1.23中引入的,Windows平台不支持该模型。
    • poll模型和select模型工作方式基本相同,区别在于,select模型创建了3个描述符集合,而poll模型只创建一个描述符集合。
  • epoll模型:
    • epoll模型属于poll模型的变种,在Linux2.5.44中引入。epoll比poll更加高效,原因在于它不需要轮询整个描述符集合,而是Linux内核会关注事件集合,当有变动时,内核会发来通知。
    • 正式这种异步,非阻塞、IO多路复用的事件驱动模型,才使得 Nginx 有很高的运行效率。

3. 案例

3.1 Nginx 进程模型实验

按照前面的讲解,我们测试给 Nginx 的 Master 进程直接发送信号,并观察进程情况:

# 确认没有 Nginx 进程
[root@server sbin]# ps -ef | grep nginx
root     10603 10137  0 14:23 pts/2    00:00:00 grep --color=auto nginx
# 启动 Nginx 
[root@server sbin]# ./nginx
# 可以看到 Nginx 启动的进程
[root@server sbin]# ps -ef | grep nginx
root     10640     1  0 14:23 ?        00:00:00 nginx: master process ./nginx
root     10642 10640  0 14:23 ?        00:00:00 nginx: worker process
root     10643 10640  0 14:23 ?        00:00:00 nginx: worker process
root     10644 10640  0 14:23 ?        00:00:00 nginx: cache manager process
root     10645 10640  0 14:23 ?        00:00:00 nginx: cache loader process

可以看到 Nginx 启动了 Master 进程(pid=10640),后由 Master 进程 fork 除了两个 Worker 进程和两个 Cache 进程,他们的父进程 id 均为10640。现在做如下几个操作:

  • 关闭进程号等于10642的 Worker 进程
[root@server sbin]# sudo kill -SIGTERM 10642
[root@server sbin]# ps -ef | grep nginx
root     10640     1  0 14:23 ?        00:00:00 nginx: master process ./nginx
root     10643 10640  0 14:23 ?        00:00:00 nginx: worker process
root     10644 10640  0 14:23 ?        00:00:00 nginx: cache manager process
root     10869 10640  0 14:32 ?        00:00:00 nginx: worker process
root     10939 10137  0 14:32 pts/2    00:00:00 grep --color=auto nginx

可以看到原先的 Worker 进程被杀死后,Nginx 的主进程又立马拉起来一个新的 Worker 进程提供服务。这说明 Nginx 是非常可靠的,只要 Master 进程还在就会保证 Worker 进程持续存在并提供服务。

  • 向主进程发送 SIGHUP 信号,等价于 -s reload 操作。可以看到除了 Master 进程外,所有其他进程已经是新启动的进程了。
[root@server sbin]# sudo kill -SIGHUP 10640
[root@server sbin]# ps -ef | grep nginx
root     10640     1  0 14:23 ?        00:00:00 nginx: master process ./nginx
root     11059 10640  0 14:37 ?        00:00:00 nginx: worker process
root     11060 10640  0 14:37 ?        00:00:00 nginx: worker process
root     11061 10640  0 14:37 ?        00:00:00 nginx: cache manager process
root     11098 10137  0 14:37 pts/2    00:00:00 grep --color=auto nginx
  • 向主进程发生 SIGTERM 信号,等价于 -s stop 操作,即停止 Nginx 服务,关闭所有进程
[root@server sbin]# sudo kill -SIGTERM 10640
[root@server sbin]# ps -ef | grep nginx
root     11267 10137  0 14:43 pts/2    00:00:00 grep --color=auto nginx
  • 向主进程发生 USR1 信号,等价于 -s repoen 操作,即重新打开日志文件
[root@server sbin]# ./nginx

[root@server sbin]# ps -ef | grep nginx
root     11408     1  0 14:48 ?        00:00:00 nginx: master process ./nginx
root     11410 11408  0 14:48 ?        00:00:00 nginx: worker process
root     11411 11408  0 14:48 ?        00:00:00 nginx: worker process
root     11412 11408  0 14:48 ?        00:00:00 nginx: cache manager process
root     11413 11408  0 14:48 ?        00:00:00 nginx: cache loader process
root     11484 10137  0 14:49 pts/2    00:00:00 grep --color=auto nginx

[root@server sbin]# ll ../logs/access.log 
-rw-r--r-- 1 root root 384349 Feb 11 14:26 ../logs/access.log

[root@server sbin]# rm -rf ../logs/access.log 

[root@server sbin]# ll ../logs/access.log 
ls: cannot access ../logs/access.log: No such file or directory

[root@server sbin]# kill -USR1 11408
[root@server sbin]# ll ../logs/access.log 
-rw-r--r-- 1 root root 0 Feb 11 14:50 ../logs/access.log

可以看到,在移除 Nginx 的 access.log 日志后,在向 Nginx 主进程发送 USR1 信号,Nginx 会重新生成一个新的 access.log 日志。

4. 小结

本节主要介绍 Nginx 的进程模型以及介绍了 Master 和 Worker 之间的通信过程。此外,我们介绍了 Nginx 的事件驱动模型。异步、多路IO复用、非阻塞等特点早就了 Nginx 的高性能。接下来,我们完成了 Nginx 进程模型的几个实验,直观体检 Nginx 的进程模型。下一节将重点介绍 Nginx 模块相关的内容,并手动实现一个简单的 http 模块。