Node.js 生产运行:优雅退出、信号与 twelve-factor 心智
本地
node app.js一停就完事;线上是 多副本、滚动发布、健康检查、配置注入。Node 进程 怎么起、怎么停、怎么证明自己还活着,和代码逻辑一样影响可用性。
这一篇想讲清楚几件事:SIGTERM / SIGINT 在生产里意味着什么、什么叫优雅关闭、以及 twelve-factor 里和 Node 最相关的几条怎么落地。
进程不是「永动机」:会收到结束信号
在容器与 systemd 环境里,停止服务 常见路径是:
- 先发
SIGTERM,给一段 宽限期; - 超时再
SIGKILL(不可捕获,直接硬杀)。
Node 里可以 process.on('SIGTERM', handler)(以及 SIGINT 用于本地 Ctrl+C)。
优雅关闭 通常包括:
- 停止接新请求(HTTP server
close); - 等已有请求收尾(或限时);
- 关掉数据库连接池、定时器、子进程;
- 最后
process.exit(或由运行时自然退出)。
若忽略 SIGTERM、或关闭逻辑 超过编排系统的 kill 时间,会被硬杀 → 事务半截、连接泄漏、用户看到 502。
HTTP Server 的 close:不再 accept,尽量排完队
server.close(callback):
- 不再接受新连接;
- 已有连接 仍由你决定是否等它们结束。
和 server.closeAllConnections()(较新 API,视版本)之类手段的差别是:
- 前者偏 温和;
- 紧急腾退时可能需要 更硬的断连策略(要接受客户端重试)。
长连接(WebSocket、gRPC)要 单独协议层 通知客户端重连。
健康检查:liveness 与 readiness
编排里常见两个探针:
- Liveness:进程 死没死——卡死也要被拉起来;
- Readiness:能不能接流量——依赖没好就先别进负载均衡。
Node 服务可以:
- 轻量 HTTP 路径(如
/healthz)只做 本进程心跳; - readiness 里 查一下 DB/Redis(注意 超时与失败策略,别反把事件循环拖死)。
别把 重业务逻辑 塞进健康检查。
twelve-factor 里和 Node 最贴的几条
配置(Config):从 环境变量 注入,不把密钥写进镜像;与 安全 那篇一致。
日志(Logs):stdout/stderr 流,由平台收集;应用里 不要绑死文件路径(除非明确要落盘且可轮转)。
端口绑定(Port binding):服务 监听 process.env.PORT,由外面注入。
进程(Processes):无状态、可水平扩展;会话进 Redis 等外置存储。
管理进程(Admin processes):migration、一次性任务 用同一套 release、同一环境,不要手敲生产机器上的神秘命令。
崩溃与重启:退出码与「让它挂」
非零退出码 告诉编排 重启副本。
策略上:
- 不可恢复错误(配置缺失、依赖版本不对)应 快速失败;
- 瞬时错误(网络闪断)可用 重试 + 熔断,但要 上限,避免死循环刷日志。
uncaughtException 里继续跑 要谨慎——状态可能已坏,记录后退出 往往更安全(由平台拉起新进程)。
小结:生产运行 = 可观测 + 可替换 + 可体面收场
- 信号 是编排和你的 合约,要有 超时内有界的关闭路径;
server.close+ 资源清理 是优雅退出的骨架;- 健康检查 要 轻、快、语义分清;
- twelve-factor 把 配置、日志、端口、无状态 从代码里 剥离 成环境能力。
把进程当成 可随时被换掉的 cattle,而不是 碰不得的 pet,和 调试观测、子进程 两篇一起看,部署图就完整一截。