Node.js 服务的日志、健康检查与优雅退出

把请求日志、requestId、健康检查、进程信号处理与优雅退出放回同一条服务稳定性主线中。

#type / synthesis #status / growing #tech / dev / backend #resource / nodejs #topic / reliability

[!info] related notes

Node.js 服务的日志、健康检查与优雅退出

范围

这篇聚焦服务稳定性的三块基础能力:

  • 日志与 requestId
  • 健康检查
  • 收到退出信号后的优雅关闭

为什么要放在一起理解

这些能力的共同目标不是“功能开发”,而是:

服务出了问题时能定位,发布和重启时能收口,故障时能减少请求中断与数据不一致。

依赖路径 / 调用链 / 演进链

可以把一条请求在服务里的稳定性链路理解成:

  1. 请求进入时生成 requestId
  2. 日志记录入口、错误、耗时和关键上下文
  3. 平台通过健康检查判断实例是否可接流量
  4. 实例下线时先停止接新流量,再等旧请求收尾,最后关闭连接退出

对比与易混淆点

日志

至少要能记录:

  • requestId
  • userId
  • method / url
  • status
  • duration
  • error stack

如果要进一步工程化,通常还会区分:

  • access log
  • application log
  • error log

这样查询问题时不会把所有信息混成一锅。

健康检查

健康检查不是“返回个 200 就行了”。

更稳的是区分:

  • 存活检查:进程还活着吗
  • 就绪检查:数据库、Redis、关键依赖可用吗,实例现在能不能接流量

一个最容易出事故的误区是:

只做 liveness,不做 readiness。

这样实例虽然“没死”,但数据库还没连上、迁移还没跑完、Redis 还没就绪时,也可能被流量打进来。

优雅退出

优雅退出通常包含:

  1. 收到 SIGTERM
  2. 不再接受新请求
  3. 等已有请求处理完
  4. 关闭数据库、Redis、消息消费者
  5. 超时仍未结束就强制退出

一个最小 Node.js 代码骨架通常类似:

const server = app.listen(3000);

process.on('SIGTERM', () => {
  server.close(async () => {
    await db.close();
    await redis.quit();
    process.exit(0);
  });

  setTimeout(() => {
    process.exit(1);
  }, 10000);
});

这里的重点不是背 API,而是讲清:

  • 先停新请求
  • 再收尾旧请求
  • 再关依赖
  • 最后退出进程

为什么这三件事应该一起理解

如果只有日志,没有健康检查,你能看到问题但平台不一定知道实例该不该接流量。

如果只有健康检查,没有优雅退出,发布重启时仍可能把正在处理的请求直接掐断。

如果只有优雅退出,没有 requestId 和结构化日志,退出阶段的问题又很难追。

一个最稳的面试表达

我会把服务稳定性拆成三层:请求进入时通过 requestId 做日志链路,运行中通过健康检查暴露实例状态,下线时通过优雅退出先停新流量再收尾旧请求,避免请求中断和资源泄漏。

最小面试回答模板

如果被追问“上线后怎么保证服务稳定”,可以答:

我会先做结构化日志和 requestId,保证链路能查;再做 liveness/readiness 健康检查,让平台知道实例现在能不能接流量;最后做优雅退出,收到 SIGTERM 后先 stop accept、再 drain in-flight request、再关闭数据库和 Redis,避免发布或扩缩容时把请求直接掐断。

创建于 2026/5/21 更新于 2026/5/27