Shutdown Check

搜索文档

查找页面或章节

分清谁负责实现关闭、谁负责测试,以及每种方式能证明什么。

关闭库和关闭测试解决的是不同的问题。库负责给你的服务加上信号处理和清理逻辑;shutdown-check 则启动真实的服务,从外部测试这些逻辑是否真的生效。

shutdown-check 适合做什么?

如果你已经有信号处理函数(不管是自己写的、框架提供的,还是来自某个库),并且想验证进程完整的生命周期,就可以用 shutdown-check。

它会检查:

  • 接近生产环境的启动命令
  • 就绪检查
  • 一个真实的、正在处理的 HTTP 请求
  • SIGTERM 的送达
  • 响应是否完成
  • 可选的流量排空
  • 进程退出和退出码
  • 最终端口是否关闭

它不会替你注册信号处理函数,也不会替你关闭应用的资源。

关闭库

@godaddy/terminus、http-terminator、lightship、close-with-grace 这类库运行在应用内部。它们可能会注册信号监听器、关闭 HTTP 服务器、管理超时,并提供清理钩子。

需要一套关闭实现时,就用库;配置好之后,再用 shutdown-check 测试最终效果。库本身没问题,也可能被用错:关闭的可能不是正确的 server 对象,清理的顺序可能不对,或者生产环境的启动命令可能让信号到不了 Node.js。

框架的关闭钩子

Fastify、NestJS 等框架都提供了关闭相关的 API。它们同样是实现,而不是端到端测试。

框架层面的测试通常直接调用 close 方法,而部署时是向进程发送信号。shutdown-check 把信号监听器、启动命令、框架、打开的 socket 和最终退出放在一起覆盖。

单元测试

单元测试可以调用关闭函数,并断言 mock 的依赖都已经关闭。

优点:

  • 快
  • 失败时能精确定位
  • 容易覆盖错误分支
  • 适合测试数据库和队列的清理逻辑

局限:

  • 通常没有真实的子进程
  • 没有操作系统信号
  • HTTP 连接是 mock 的
  • 无法证明生产环境的启动命令会退出
  • 不检查是否留下孤儿子进程

单元测试要保留。再补一个黑盒测试,覆盖单元测试测不到的进程行为。

用 kill 和 curl 写的 shell 脚本

自己写脚本也可以:启动应用,用 curl 发请求,发送 kill -TERM,再检查退出情况。

这种做法行得通,但很难做到可靠的同步。固定的 sleep 并不能证明信号到达时请求正在处理。脚本还得自己处理并发请求、响应体、超时、进程组、清理、退出码和 CI 报告。

shutdown-check 把这些细节封装成一份可重复运行的配置和一套固定不变的诊断码。

预发布环境与 Kubernetes 测试

集群层面的行为只能在预发布环境(staging)里测试,比如 endpoint 的传播、ingress 的时序、sidecar 和 preStop 钩子。

但预发布测试也更慢,更难复现。终止时不一定有请求正在处理,而一次成功的发布也无法证明没有丢请求。

部署前,先用 shutdown-check 测试服务本身的行为,结果稳定、可复现;之后再到预发布环境测试基础设施层面的行为。

对比表

能力shutdown-check关闭库单元测试Shell 脚本预发布测试
添加关闭逻辑否是否否否
运行真实的启动命令是不适用通常否是是
发送真实的 SIGTERM是负责处理通常否是是
证明发信号前请求正在处理是否借助 mock困难困难
验证响应状态码和响应体是否是可以做到可以做到
检查就绪状态撤回可选可能提供实现借助 mock可以做到是
检查进程退出和最终端口是否否可以做到间接
直接检查数据库和队列否可能负责关闭是需自行实现需自行实现
覆盖集群路由和 sidecar否否否否是
输出固定的失败码是否测试名称需自行实现需自行实现

应该选哪种方式?

大多数服务可以这样做:

  1. 用框架自带的功能、某个库或一个简单的处理函数来实现关闭。
  2. 用单元测试覆盖应用特有的清理逻辑和错误路径。
  3. 在本地和 CI 中运行 shutdown-check,测试进程和 HTTP 的生命周期。
  4. 在预发布环境中测试 Kubernetes、代理和负载均衡器的行为。

这几层是互补的。只用其中一种测试替代全部,会让一些重要行为得不到验证。

shutdown-check 不够用的情况

如果正确性依赖以下内容,还需要补充其他测试:

  • HTTP 响应之后的数据库事务
  • 队列消息确认(ack)
  • 后台任务
  • WebSocket 或 HTTP/2
  • 与远程服务的协调
  • 容器或集群的终止顺序

具体边界见兼容性说明。