Node.js 服务的日志、健康检查与优雅退出
把请求日志、requestId、健康检查、进程信号处理与优雅退出放回同一条服务稳定性主线中。
[!info] related notes
- 所属 MOC: Node.js 后端面试 MOC, Node.js MOC
- 相关概念: winston, 后端测试, 接口幂等设计
- 相关资源: nodejs
Node.js 服务的日志、健康检查与优雅退出
范围
这篇聚焦服务稳定性的三块基础能力:
- 日志与 requestId
- 健康检查
- 收到退出信号后的优雅关闭
为什么要放在一起理解
这些能力的共同目标不是“功能开发”,而是:
服务出了问题时能定位,发布和重启时能收口,故障时能减少请求中断与数据不一致。
依赖路径 / 调用链 / 演进链
可以把一条请求在服务里的稳定性链路理解成:
- 请求进入时生成 requestId
- 日志记录入口、错误、耗时和关键上下文
- 平台通过健康检查判断实例是否可接流量
- 实例下线时先停止接新流量,再等旧请求收尾,最后关闭连接退出
对比与易混淆点
日志
至少要能记录:
- requestId
- userId
- method / url
- status
- duration
- error stack
如果要进一步工程化,通常还会区分:
- access log
- application log
- error log
这样查询问题时不会把所有信息混成一锅。
健康检查
健康检查不是“返回个 200 就行了”。
更稳的是区分:
- 存活检查:进程还活着吗
- 就绪检查:数据库、Redis、关键依赖可用吗,实例现在能不能接流量
一个最容易出事故的误区是:
只做 liveness,不做 readiness。
这样实例虽然“没死”,但数据库还没连上、迁移还没跑完、Redis 还没就绪时,也可能被流量打进来。
优雅退出
优雅退出通常包含:
- 收到
SIGTERM - 不再接受新请求
- 等已有请求处理完
- 关闭数据库、Redis、消息消费者
- 超时仍未结束就强制退出
一个最小 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,避免发布或扩缩容时把请求直接掐断。