0

0

nginx信号集实例分析

WBOY

WBOY

发布时间:2023-05-13 10:37:16

|

1134人浏览过

|

来源于亿速云

转载

场景复现

下面我将使用一个原生的 nginx,在我的安装了 fedora26 的虚拟机上复现这个过程,我使用的 nginx 版本是目前最新的 1.13.4

首先启动 nginx

nginx信号集实例分析

可以看到 master 和 worker 都已经在运行。

接着我们向 master 发送一个 sigusr2 信号,当 nginx 核心收到这个信号后,就会触发热更新。

nginx信号集实例分析

可以看到新的 master 和该 master fork 出来的 worker 已经在运行了,此时我们接着向旧 master 发送一个 sigwinch 信号,旧 master 收到这个信号后,会向它的 worker 发送 sigquit,于是旧 master 的 worker 进程就会退出:

nginx信号集实例分析

此时只剩下旧的 master,新的 master 和新 master 的 worker 在运行,这和当时线上运行的情况类似。

接着我们使用 stop 命令:

nginx信号集实例分析

我们会发现,新的 master 和它的 worker 都已经退出,而旧的 master 还在运行,并产生了 worker 出来。这就是当时线上的情况了。

事实上,这个现象和 nginx 自身的设计有关:当旧的 master 准备产生 fork 新的 master 之前,它会把 nginx.pid 这个文件重命名为 nginx.pid.oldbin,然后再由 fork 出来的新的 master 去创建新的 nginx.pid,这个文件将会记录新 master 的 pid。nginx 认为热更新完成之后,旧 master 的使命几乎已经结束,之后它随时会退出,因此之后的操作都应该由新 master 接管。当然,在旧 master 没有退出的情况下通过向新 master 发送 sigusr2 企图再次热更新是无效的,新 master 只会忽略掉这个信号然后继续它自己的工作。

问题分析

更不巧的是,我们上面提到的这个 lua table,定义它的 lua 文件早在运行 init_by_lua 这个 hook 的时候,就已经被 luajit 加载到内存并编译成字节码了,那么显然旧的 master 必然没有这个 lua table,因为它加载那部分 lua 代码是旧版本的。

而索引该 table 的 lua 代码并没有在 init_by_lua 的时候使用到,这些代码都是在 worker 进程里被加载起来的,这时候项目目录里的代码都是最新的,所以 worker 进程加载的都是最新的代码,如果这些 worker 进程处理到相关的请求,就会出现 lua 运行时错误,外部表现则是对应的 http 500。

吸收了这个教训之后,我们需要更加合理地关闭我们的 nginx 服务。 所以一个更加合理的 nginx 服务启动关闭脚本是必需的,网上流传的一些脚本并没有对这个现象做处理,我们更应该参考 nginx 官方提供的脚本。

nginx信号集实例分析

这段代码引自 nginx 官方的 /etc/init.d/nginx 。

nginx 信号集

接下来我们来全面梳理下 nginx 信号集,这里不会涉及到源码细节,感兴趣的同学可以自行阅读相关源码。

我们有两种方式来向 master 进程发送信号,一种是通过 nginx -s signal 来操作,另一种是通过 kill 命令手动发送。

绘蛙
绘蛙

电商场景的AI创作平台,无需高薪聘请商拍和文案团队,使用绘蛙即可低成本、批量创作优质的商拍图、种草文案

下载

第一种方式的原理是,产生一个新进程,该进程通过 nginx.pid 文件得到 master 进程的 pid,然后把对应的信号发送到 master,之后退出,这种进程被称为 signaller。

第二种方式要求我们了解 nginx -s signal 到真实信号的映射。下表是它们的映射关系:

operation signal
reload sighup
reopen sigusr1
stop sigterm
quit sigquit
hot update sigusr2 & sigwinch & sigquit
stop vs quit

stop 发送 sigterm 信号,表示要求强制退出,quit 发送 sigquit,表示优雅地退出。 具体区别在于,worker 进程在收到 sigquit 消息(注意不是直接发送信号,所以这里用消息替代)后,会关闭监听的套接字,关闭当前空闲的连接(可以被抢占的连接),然后提前处理所有的定时器事件,最后退出。没有特殊情况,都应该使用 quit 而不是 stop。

reload

master 进程收到 sighup 后,会重新进行配置文件解析、共享内存申请,等一系列其他的工作,然后产生一批新的 worker 进程,最后向旧的 worker 进程发送 sigquit 对应的消息,最终无缝实现了重启操作。

reopen

master 进程收到 sigusr1 后,会重新打开所有已经打开的文件(比如日志),然后向每个 worker 进程发送 sigusr1 信息,worker 进程收到信号后,会执行同样的操作。reopen 可用于日志切割,比如 nginx 官方就提供了一个方案:

nginx信号集实例分析

这里 sleep 1 是必须的,因为在 master 进程向 worker 进程发送 sigusr1 消息到 worker 进程真正重新打开 access.log 之间,有一段时间窗口,此时 worker 进程还是向文件 access.log.0 里写入日志的。通过 sleep 1s,保证了 access.log.0 日志信息的完整性(如果没有 sleep 而直接进行压缩,很有可能出现日志丢失的情况)。

hot update

某些时候我们需要进行二进制热更新,nginx 在设计的时候就包含了这种功能,不过无法通过 nginx 提供的命令行完成,我们需要手动发送信号。

通过上面的问题复现,大家应该已经了解到如何进行热更新了,我们首先需要给当前的 master 进程发送 sigusr2,之后 master 会重命名 nginx.pid 到 nginx.pid.oldbin,然后 fork 一个新的进程,新进程会通过 execve 这个系统调用,使用新的 nginx elf 文件替换当前的进程映像,成为新的 master 进程。新 master 进程起来之后,就会进行配置文件解析等操作,然后 fork 出新的 worker 进程开始工作。

接着我们向旧的 master 发送 sigwinch 信号,然后旧的 master 进程则会向它的 worker 进程发送 sigquit 信息,从而使得 worker 进程退出。向 master 进程发送 sigwinch 和 sigquit 都会使得 worker 进程退出,但是前者不会使得 master 进程也退出。

最后,如果我们觉得旧的 master 进程使命完成,就可以向它发送 sigquit 信号,让其退出了。

worker 进程如何处理来自 master 的信号消息

实际上,master 进程再向 worker 进程通讯,不是使用 kill 函数,而是使用了通过管道实现的 nginx channel,master 进程向管道一端写入信息(比如信号信息),worker 进程则从另外一端收取信息,nginx channel 事件,在 worker 进程刚刚起来的时候,就被加入事件调度器中(比如 epoll,kqueue),所以当有数据从 master 发来时,即可被事件调度器通知到。

nginx 这么设计是有理由的,作为一个优秀的反向代理服务器,nginx 追求的就是极致的高性能,而 signal handler 会中断 worker 进程的运行,使得所有的事件都被暂停一个时间窗口,这对性能是有一定损失的。

很多人可能会认为当 master 进程向 worker 进程发送信息之后,worker 进程立刻会有对应操作回应,然而 worker 进程是非常繁忙的,它不断地处理着网络事件和定时器事件,当调用 nginx channel 事件的 handler 之后,nginx 仅仅只是处理了一些标志位。真正执行这些动作是在一轮事件调度完成之后。所以这之间存在一个时间窗口,尤其是业务复杂且流量巨大的时候,这个窗口就有可能被放大,这也就是为什么 nginx 官方提供的日志切割方案里要求 sleep 1s 的原因。

当然,我们也可以绕过 master 进程,直接向 worker 进程发送信号,worker 可以处理的信号有

signal effect
sigint 强制退出
sigterm 强制退出
sigquit 优雅退出
sigusr1 重新打开文件

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

38

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

83

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

97

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

223

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

458

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

169

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

246

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

34

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.4万人学习

nginx浅谈
nginx浅谈

共15课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号