hqbsh.com 运行时间
HQBSH.com的whois记录显示注册于2013年1月18日,至今已经持续运营了:0年0个月0天零0小时0分钟0秒

最新报价
 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 145|回复: 0

Nginx Stop 信号对比:TERM 与 QUIT 的本质差异

[复制链接]

163

主题

0

回帖

139

银子

超级版主

积分
3567
发表于 2026-6-22 06:05 | 显示全部楼层 |阅读模式
本帖最后由 dctc_shouhuzhe 于 2026-8-9 14:40 编辑

说真的,Nginx 的进程管理信号这件事,初次接触的人很容易被 nginx -s stopnginx -s quitnginx -s reloadnginx -s reopen 这四个长得几乎一模一样的命令绕晕。表面上看都是「让 Nginx 干点啥」,但底层走的信号完全不同,对线上流量的影响也天差地别。

SIGQUIT
老规矩,我先把结论摆前面:生产环境里,只要不是要立刻让 Nginx 消失(比如机器要关机、配置出现严重问题需要紧急止血),首选永远是 nginx -s quit。这条命令会走 SIGQUIT 信号,Nginx 会等所有活跃连接正常处理完再退出,不会让你的用户收到莫名其妙的连接重置。

下面我从信号层面把这事儿掰开讲清楚,顺便把 reload 和 reopen 也一起讲了,免得你下次又踩坑。


一、四种控制信号的速查表

先上对比表,这张表是全文的锚点,建议收藏:

控制方式底层信号信号编号等价命令行为特征
快速停止(Stop)SIGTERM15kill -TERM 立即终止,不等待活跃连接释放
优雅停止(Quit)SIGQUIT3kill -QUIT 等待所有活跃连接处理完毕后再退出
配置重载(Reload)SIGHUP1kill -HUP 重新加载配置,新建 Worker,老 Worker 处理完存量请求后退出
日志切割(Reopen)SIGUSR110kill -USR1 重新打开日志文件,用于 logrotate 场景

注意:网上不少老文章把 SIGQUIT 写成「信号 0」,这是错的。信号 0 在 POSIX 里是「空信号」,根本不会传递给进程,只用于 kill -0 检查进程是否存在。真正的 SIGQUIT 编号是 3,SIGTERM 是 15。表格里我直接列正确编号,避免你照着错的去 kill。


二、SIGTERM 的处理逻辑:为什么说它是「粗暴」的停止方式

Nginx 是典型的 Master/Worker 进程模型(这玩意儿到现在也是 Web 服务器里的经典设计,参考 Nginx、Apache、HAProxy 都是这套思路)。理解信号差异之前,必须先把这个模型刻进脑子里:

  • Master 进程:只有一个,负责读取配置、管理 Worker、接收外部信号
  • Worker 进程:默认有多个(一般等于 CPU 核数),实际处理 HTTP 请求

当你在终端执行 nginx -s stop 时,Nginx 客户端工具会向 Master 进程发送 SIGTERM。接下来的链路是这样的:

  1. Master 进程收到 SIGTERM,不再走任何优雅退出逻辑
  2. Master 立刻向所有 Worker 进程转发 SIGTERM
  3. Worker 进程不做任何清理,直接退出(不关闭 socket、不等待请求完成)
  4. 此时正在处理的 HTTP 请求被强制中断,客户端大概率看到「Connection reset by peer」或者响应截断

老实讲,这种行为在生产环境是相当不友好的。如果你的 Nginx 前端还有负载均衡,连接重置会被打到后端服务上,相当于一次小规模的事故。所以 SIGTERM 这个信号,更适合「机器要断电了」「Nginx 配置写错导致整个服务雪崩」这种紧急止血场景。


三、SIGQUIT 的优雅之处:Graceful Shutdown 完整流程

跟 SIGTERM 形成鲜明对比的是 SIGQUIT。nginx -s quit 走的是这条路径:

  1. Master 进程收到 SIGQUIT,关闭监听 socket(关键:这一步让新连接不再进来,但已建立的连接不受影响)
  2. Master 向所有 Worker 进程发送 SIGQUIT
  3. Worker 进程进入「优雅退出」状态:不再接受新连接,但会继续处理完当前正在处理的请求
  4. Worker 处理完所有活跃请求后,正常关闭连接,退出进程
  5. 所有 Worker 退出后,Master 自己也退出

这套流程在 Nginx 源码里被称为 Graceful Shutdown。说白了,Nginx 给了一个「自然消亡」的窗口期,让正在进行的业务跑完再下线。

这里有个很多人会忽略的细节:Worker 在优雅退出时,会先处理完 worker_shutdown_timeout 配置定义的超时时间内的请求(这个配置在 nginx.conf 里,默认无超时,部分高版本 Nginx 引入)。如果你的接口处理时间特别长(比如大文件上传、AI 推理接口),需要合理设置这个值,否则 Worker 会一直挂着不退出,导致 stop 脚本超时被 systemd 强杀。

四、SIGHUP 和 SIGUSR1:别再把 reopen 当成 reload

很多运维新手会把 nginx -s reopennginx -s reload 混为一谈,毕竟名字都长得很像。但它们的底层信号完全不同,作用场景也完全不一样:

SIGHUP(Reload)— 配置热加载

这是大家最熟悉的信号。nginx -s reload 走的是 SIGHUP,Master 收到后的处理流程:

  1. 重新读取 nginx.conf,校验语法
  2. 如果配置有问题,Master 直接报错,旧的 Worker 继续服务(这点很关键,不会因为 reload 把服务搞挂)
  3. 配置没问题的话,根据新配置启动新的 Worker 进程
  4. 老 Worker 收到关闭信号,等手里的请求处理完再退出
  5. 新 Worker 开始接受新连接

这个流程就是传说中的「零停机热加载」,是 Nginx 在配置变更场景的核心能力。

SIGUSR1(Reopen)— 日志切割专用

nginx -s reopen 走的是 SIGUSR1,作用是让 Nginx 重新打开日志文件。典型使用场景是配合 Linux 的 logrotate 做日志切割:

# /etc/logrotate.d/nginx 大致写法
/var/log/nginx/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 0640 nginx nginx
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
    endscript
}

为什么要单独搞个 reopen?因为 Linux 下文件被打开后,即使你用 mv 把日志移走,进程里的文件描述符还是指向旧 inode,新请求的日志会继续写到被 mv 走的旧文件里。reopen 就是让 Nginx 关闭旧 fd、重新打开同名新文件,确保日志切割后新日志能正确写入。


五、生产环境选型建议(这部分建议收藏)

基于上面四节的原理分析,结合我自己在生产环境的踩坑经验,给出几条可直接落地的建议:

1. 重启/升级 Nginx → nginx -s quit,不是 stop

# 推荐写法
nginx -t && nginx -s quit

nginx -t 先校验配置语法,没问题再 quit。这套组合拳是滚动发布、配置变更、二进制升级(USR2 + QUIT)的标准动作。

2. systemd 环境下,注意默认 KillSignal

CentOS 7+ / Ubuntu 16+ 默认 systemd 管理 Nginx。如果你在 service 文件里没改 KillSignal,systemd 给 Nginx 的默认停止信号是 SIGTERM,等同于 nginx -s stop,不是优雅停止。

推荐在 /etc/systemd/system/nginx.service/lib/systemd/system/nginx.service 里加上:

[Service]
KillSignal=SIGQUIT
TimeoutStopSec=30

TimeoutStopSec=30 给 30 秒的优雅退出窗口,配合 Nginx 自己的 worker_shutdown_timeout 一起用,防止长时间运行的请求被强杀。

3. 容器化部署(Docker/K8s)的特殊情况

容器场景下,Nginx 通常跑在前台(nginx -g "daemon off;"),K8s 终止 Pod 时默认发的是 SIGTERM,Docker stop 默认也是 SIGTERM(10 秒后转 SIGKILL)。

这意味着如果你不改配置,容器里 Nginx 退出时也是粗暴停止。两个解决方案:

  • 启动命令改成 nginx -g "daemon off; signal-stop; signal-quit;"(Nginx 1.x 早期版本不支持,需根据实际版本调整)
  • 自己写个 entrypoint.sh 捕获 SIGTERM 后转发为 SIGQUIT 再退出
  • 在 Dockerfile 里用 STOPSIGNAL SIGQUIT 指令改默认停止信号(Dockerfile 17.05+ 支持)

K8s 侧记得把 terminationGracePeriodSeconds 调大,给 Nginx 足够的退出时间。

4. 紧急情况才用 SIGTERM

机器要关机、Nginx 因为配置错误挂了、Worker 陷入死循环无法响应——这些场景下 kill -TERM 是最快止血手段。但事后一定要复盘:是不是该上配置校验、是不是该加 worker_rlimit_core、是不是该用 stub_status 监控。


六、常见问题 FAQ

Q1:怎么查看当前 Nginx Master 进程的 PID?
cat /var/run/nginx.pid
# 或者
ps aux | grep "nginx: master process"
Q2:执行 quit 之后 Worker 一直没退出,进程还在怎么办?
大概率是某个 Worker 卡在长请求上(文件上传、WebSocket、AI 推理)。先 nginx -s quit 给 30 秒窗口,如果还没退,再 kill -TERM 强杀。如果你经常碰到这种情况,考虑给接口加超时、把 Nginx 上游超时调小,或者在前端做请求大小限制。
Q3:reload 之后老的 Worker 一直不退,正常吗?
正常。reload 走的就是「老 Worker 处理完存量请求再退」的优雅流程。如果老 Worker 长期不退,说明你的接口里有长连接(WebSocket、Server-Sent Events、文件下载)。这种情况是设计上的合理现象,不是 bug。
Q4:QUIT 和 HUP 的「优雅」有什么区别?
QUIT 是「整体下线」前的优雅,所有 Worker 最终都会退出;HUP 是「配置变更」前的优雅,新 Worker 会顶上接管流量,老 Worker 自然消亡。一个是退役,一个是换班。
Q5:能在不重启的情况下单独让某个 Worker 退出吗?
不能。Nginx 没有提供按 Worker 维度单独管理的接口,所有信号都是面向 Master 进程的。如果你需要对单个 Worker 做隔离,得考虑在前面挂负载均衡,把流量摘掉再处理。
Q6:SIGKILL(信号 9)能用吗?
能用,但不建议。SIGKILL 不能被捕获,Nginx 没机会做任何清理——不释放 socket、不关闭连接、不刷日志、不通知上游。生产环境上 SIGKILL 基本上等同于一次小型事故现场,只有在 SIGTERM 都杀不掉进程(极端僵尸状态)时才作为最后手段。

七、最后总结

四种信号,一张图带过:

  • SIGTERM(stop):粗暴中断,紧急止血用
  • SIGQUIT(quit):优雅退出,正常停机/升级用
  • SIGHUP(reload):热加载配置,零停机变更用
  • SIGUSR1(reopen):重新打开日志,logrotate 配合用

记住这张表和这套对应关系,下次再遇到 Nginx 进程管理的问题,基本都能直接定位。线上服务无小事,stop 和 quit 用错一次,可能就是一个工单。


标签:Nginx信号机制SIGTERMSIGQUITSIGHUPSIGUSR1优雅停止Graceful ShutdownLinux 运维Web 服务器负载均衡进程模型

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

 
 
加好友78950405
QQ臨時會話
華強北商行笔记本,手機
淘宝阿里旺旺
沟通交流群:
水货thinkpad笔记本
工作时间:
11:00-22:00
电话:
18938079527
微信联系我们

QQ|手机版|华强北商行 ( 粤ICP备17062346号 )

JS of wanmeiff.com and vcpic.com Please keep this copyright information, respect of, thank you!JS of wanmeiff.com and vcpic.com Please keep this copyright information, respect of, thank you!

|网站地图 手机端 公司简介 联系方式 版权所有@

GMT+8, 2026-8-9 21:46 , Processed in 0.013158 second(s), 6 queries , Redis On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表