Author: haoransun
Wechat: SHR—97
学习来源:极客时间-Nginx核心知识100讲,本人购买课程后依据视频讲解汇总成个人见解。
前言
为什么要讨论Nginx的架构技术呢?
因为Nginx运行在企业内网的最外层-边缘节点,它处理的流量是其他应用服务器的几个数量级。任何一种问题在不同的场景下解决方式不尽相同。因此,Nginx处理问题时的难度会被放大,我们有必要熟知:
为什么Nginx采用Master-Worker这样一种架构模型
为什么Worker进程的数量要与CPU的核数相匹配
在多个Worker之间共享数据时,为什么在TLS或者限流、限速场景下的共享方式是有所不同的。
这些都需要我们对Nginx架构有一个清晰的了解。
Nginx的请求处理流程
网络大致有三种流量 Web、Email、TCP 。
三种状态机:处理Tcp/Ip的第四层状态机、HTTP应用层状态机、mail邮件状态机。为什么需要状态机呢?因为Nginx中是使用 非阻塞的事件驱动处理引擎来工作的,即Epoll,当使用异步时,通常需要用状态机将请求正确的识别处理。
Nginx进程结构
1 单进程结构
不适用于生产环境,只适合做开发、调试。
生产环境要保持Nginx足够健壮,同时发挥Nginx可利用多核的特性。
默认配置都是打开多进程Nginx
2 多进程结构
为什么是多进程而不是多线程呢?
Nginx要保证它的高可用性、高可靠性。当Nginx使用多线程结构时,因为线程间是共享同一个地址空间的,当某一个第三方模块引发了地址空间的越界时,会导致整个nginx进程挂掉。多进程结构则不会出现上述问题。
Nginx在做进程设计时,同样遵循了高可用、高可靠原则。
在Master进程中,通常第三方模块是不会在这里加入自己的功能代码的。
虽然Nginx在设计时,允许第三方模块在Master进程中,添加自己独有的自定义的一些方法,但是第三方模块通常不会这么做。
Master进程是被设计用来做 Worker进程的管理的。
Worker进程是真正处理请求的,而Master进程是负责监控每个Worker进程是否正常工作、需不需要重新载入配置文件、热部署等。
缓存是在多个Worker进程间共享,而且还要被 Cache Manager/loader进程使用。
Cache Manager/loader进程是为反向代理时后端发过来的动态请求做缓存负责的。Manager负责缓存的管理。 Loader负责缓存的载入。
实际上每一个请求处理时用到的缓存还是由Worker进程响应的。
进程间的通讯都是使用共享内存解决。
为什么worker进程很多呢?
Nginx采用事件驱动模型后,希望每一个Worker进程从头到尾占有一颗CPU,因此,通常不仅要把Worker进程数量与服务器上的CPU核数一致,还要把每一个Worker进程与它对应的CPU绑定在一起。可以更好地使用每颗CPU核上对应的CPU缓存来减少缓存失效的命中率。
3 多进程结构实例演示
1 | 进入 openstry的安装目录 |
1 首次查看
第一次:1个master(12906) + 2个worker(父进程是12906,即master进程)
现在使用 -s reload 会将之前的 worker + cache进程 优雅的退出,重载新的配置项,新起新的 worker 与 cache进程
1 | sbin/nginx -s reload |
2 reload后查看
第二次:master不变,worker 新起
3 发送hup信号查看
第三次: reload 与 hup的作用相同,现在向 12906的 master进程发送 hup信号,结果依然相同。
1 | kill -SIGHUP 12906 |
master进程依然没有变,worker进程是新起的。
4 quit、stop信号查看
第四次:向一个 worker进程发送 quit信号,该worker进程再退出的同时,会自动的向他的父进程 12906 发送一个CHLD退出信号,master进程收到信号后,会新起一个worker进程,维持worker进程的进程结构。
1 | sigterm 退出信号 |
因此,命令行中的需多子命令,就是向Master进程发送信号而已。
4 进程模型
进程
master、worker、cache manager、cache loader
通讯方式
信号:TERM、INT、QUIT、HUP、USR1、USR2、WINCH
共享内存:slab内存管理、互斥锁
5 信号管理Nginx父子进程
Master进程
CHLD:当worker进程因为某些bug而停止工作时,它在退出时会向Master进程发送 CHLD信号,Master进程可以立刻通过 CHLD信号发现问题,重新拉起一个Worker进程。
TERM,INT:立刻停止Nginx进程
QUIT:优雅的停止Nginx进程
HUP:重载配置文件
USR1:重新打开日志文件,做日志文件的切割
以上可以直接在 Nginx命令行 + 特定命令 向Master进程发送。
下面两个 只能通过 Linux中的 kill + 命令发送给Master进程,即先找到 Master的 PID,对 PID 发送信号。
USR2:热部署时使用
WINCH:热部署时使用
Worker进程
通常是不会直接对Worker进程发送相应的信号的,应该将信号发送给Master进程,由Master进程管控Worker进程。
Nginx命令行
当启动Nginx后,Nginx会将它的 PID 记录到 Nginx安装目录的 logs文件夹下的 Nginx.pid文件中,记录了 Master进程的PID,再次执行 nginx -s 时,Nginx命令行会读取 Nginx.pid,向Master进程所在的PID同样的去发送对应的信号。
综上,命令行与发送信号本质是一致的
reload重载配置文件流程
背景
当我们更改了 nginx.conf 配置文件后,再次启动时都会执行 -s reload 命令。
好处:Nginx不停止服务,始终在处理新的请求,同时把Nginx的配置文件平滑的从旧的 nginx.conf 更新为新的 nginx.conf。
有时在执行完后,发现Nginx的 Worker进程变多了,这是因为老的 配置所运行的 Nginx-Worker进程长时间没有退出,当使用 stream做4层反向代理时可能会更多。
reload流程
第3步:因为所有的子进程会继承父进程已经打开的端口。
第4-5步:是为了平滑过渡,先创建新的,在慢慢关闭老的。
当有些绿色的 老 worker进程因为一些bug,长时间不退出时,也只会影响已存在的连接,不会影响新的连接。如何处理这些异常存活的worker进程呢
Nginx新的版本中,提供了一个配置项,称为 worker shutdown timeout
,即超时时间;Master进程在启动 黄色的 新的 Worker进程时,会为老的绿色的Worker进程加上一个 定时器,超时后,将依然存在的老的Worker进程强制退出掉。
热升级完整过程
背景
在Nginx不停止服务的情况下,做版本的更新。但也会遇到新的问题,如:
老的Worker进程一直退不掉。
新的Worker进程起来后出现了问题,考虑回滚等。
使用了新的Nginx.conf配置文件后,发现好多功能出现了错误,只能回滚等。
热升级流程
第一步:
将旧的 binary文件替换为新的 binary文件。之所以只替换binary文件,是因为在大部分场景下,我们新编译的Nginx文件所指定的配置选项(配置文件的目录在哪里,logs文件所在的目录在哪里),必须保持和老的Nginx是一致的,否则无法使用 nginx.conf文件。替换时要注意备份,同时,新版本的Linux中,要求覆盖一个正在使用的文件时,需要这样写命令cp -f
第二步:
向老的Master进程发送 USR2信号。此时我们是没有办法通过Nginx命令行直接用 nginx -s这个信号来处理,这是因为Nginx到目前为止还没有持这样的信号。
第三步:
老的Master进程为了给新的Master让路,修改自己的pid文件名,以方便新的Master进程创建自己的PID文件。
第四步:
新的Master进程起来后,会出现新老Master、老的Worker同时存在的情况。新的Master会去拉起新的Worker进程。
第五步:
通过查看 .oldbin 或者 ps -ef|grep nginx 查看老的Master进程号,以便向他发送WINCH信号。老的Master会优雅关闭老Worker进程。热升级已经完成。但是老的Master进程会一直存在,方便回滚。
新的Master进程是 老的Master进程的子进程。新的使用了新的binary来载入的。新老并存时,老的会在处理完请求后,关闭自己监听的端口,优雅的退出。完成后,就只剩下新的Master+Worker进程。
优雅关闭Worker进程
nginx - stop : 立即停止Nginx。
nginx - quit : 优雅关闭Nginx。
在上面的热部署、reload时都是优雅的停止Nginx,那他的过程如何呢?
优雅的关闭:即Nginx的worker进程可以识别出当前的连接没有正在处理请求,此时再次关闭它。能不能做到这一点呢?
对于有些请求,Nginx是做不到的。如:
Nginx代理 Web Socket协议时,在Web Socket通讯的 Frame帧中,Nginx是不解析这个帧的是没有办法识别的。
Nginx做TCP、UDP层的反向代理时,是没有办法识别一个请求需要经历多少报文才算是结束。
对于 HTTP请求是可以识别的。因此,优雅的关闭,主要针对HTTP请求。
第一步:
设置完定时器后,会加一个标志位,表示现在进入优雅的关闭流程了。
第二步:
保证所在的Worker进程不会再去处理新的连接。
第三步:
查看Nginx的连接池,Nginx为了保证对资源的充分利用,经常会保持一些空闲连接不断开。此时会关闭所有的空闲连接。
第四步:
用时可能会超过第一步 worker_shutdonw_timeout设置的时间。一旦超时,即使请求还没有处理完,依然会强制关闭连接。优雅的关闭只完成一半,有一部分是立即停止。