# 常见问题

> Node.js 优雅关闭常见问题：SIGTERM、server.close()、进行中的请求、各框架、Docker 与 Kubernetes，以及 shutdown-check 能测什么、不测什么。

Source: https://shutdown.jscrate.dev/zh/docs/faq
Last updated: 2026-09-23

这里整理了 Node.js 优雅关闭最常见的问题。需要完整代码或更深入的排查时，每个回答都附有对应的指南或参考页面链接。

## Node.js 中的优雅关闭是什么？

优雅关闭（graceful shutdown）是指停止服务时，不打断已经开始的工作。收到 `SIGTERM` 后，服务不再接收新工作，处理完进行中的请求，关闭共享资源，并在平台的截止时间之前退出。

这套流程和具体应用相关，Node.js 不会替你实现。你需要添加信号处理函数，或者使用专门的关闭库，然后验证行为是否符合预期。[Node.js 指南](https://shutdown.jscrate.dev/zh/docs/guides/graceful-shutdown-nodejs)提供了 node:http、Express 和 Fastify 的完整示例。

## shutdown-check 会让我的应用实现优雅关闭吗？

不会。shutdown-check 是测试工具，不是关闭处理库。它不会修改你的服务，也不在服务内部运行。

它会启动你配置的命令，发出一个真实请求，发送 `SIGTERM`，然后检查响应和进程。信号处理函数、资源关闭以及排空策略，仍然需要你自己实现和决定。

## 为什么 Node.js 收到 SIGTERM 会立即退出？

如果没有有效的监听器，信号会按默认行为终止进程，所有打开的 HTTP 连接都可能被切断。

在真正运行服务器的那个进程上注册监听器：

```js
process.on("SIGTERM", () => {
  server.close((error) => {
    if (error) process.exitCode = 1;
  });
});
```

如果 Node.js 是由 shell 或包管理器的包装进程启动的，信号可能只到达包装进程。尽量直接启动，例如 `node dist/server.js`。

## 为什么 server.close() 一直不结束？

`server.close()` 会等待活跃连接结束。以下情况会让它看起来像卡住了：

- 某个 HTTP 响应一直没有结束
- 上游服务或数据库操作挂起
- 客户端一直保持连接活跃
- 应用关闭了请求仍在使用的依赖
- 服务器关闭后，还有其他资源让 Node.js 保持运行

给回调、活跃的处理函数和每一步清理都加上日志，并为外部操作设置超时。请求一直不结束的情况见 [SC200](https://shutdown.jscrate.dev/zh/docs/codes/sc200)；请求都结束后进程仍不退出的情况见 [SC300](https://shutdown.jscrate.dev/zh/docs/codes/sc300)。

## 为什么测试提示测试请求始终未处于进行中？

说明测试请求在发送信号前就已结束，或者它的启动确认一直没有通过。没有进行中的工作，关闭测试就无法证明服务做了排空。

使用 `response-headers` 时，先发送响应头，并保持响应体不结束。使用 `probe` 时，让一个单独的路由在操作执行期间从未活跃变为活跃。详见[进行中的工作与启动确认](https://shutdown.jscrate.dev/zh/docs/in-flight-work)。

## 支持 Express、Fastify 或 NestJS 吗？

支持。shutdown-check 不依赖任何框架，它只启动一条命令，并通过本地 HTTP 通信。

- Express 使用 `app.listen()` 返回的 Node.js 服务器。
- Fastify 可以通过 `fastify.close()` 关闭。
- NestJS 需要配置关闭钩子，并等待它们执行完成。

服务需要提供就绪检查路由和测试请求路由，并能响应 `SIGTERM`。各框架的具体实现见[优雅关闭指南](https://shutdown.jscrate.dev/zh/docs/guides/graceful-shutdown-nodejs)。

## 能测试数据库、队列或 WebSocket 吗？

不能直接测试。shutdown-check 只观察服务进程和 HTTP/1 响应。它看不到数据库或队列的内部状态，也不会建立 WebSocket 或 HTTP/2 会话。

如果响应内容能证明关键工作已经完成，可以使用 `bodyIncludes`。数据库事务、队列确认（ack）、后台任务、WebSocket，以及响应之后仍在继续的工作，需要另外编写针对应用的测试。

## 能在 Windows 上运行吗？

不能原生运行。这个工具依赖 POSIX 信号和进程组，因此支持 macOS 和 Linux。

在 Windows 开发机上，可以在 WSL、Linux 容器或 Linux CI 中运行。服务和测试必须运行在同一个受支持的环境中，并且能访问本地端口。

## 能在 Docker 或 Kubernetes 中运行吗？

可以，但有限制。它可以在装有 Node.js 22 或更高版本的 Linux 容器中运行，测试的是该容器内的服务进程和本地 HTTP 行为。

它不会模拟 Kubernetes 的 endpoint 移除、`preStop`、sidecar、ingress 或集群的终止宽限期（grace period）。请参考 [Kubernetes 指南](https://shutdown.jscrate.dev/zh/docs/guides/kubernetes)了解这些设置如何对应，其余行为放到预发布环境中测试。

## 它和 terminus 这类关闭库有什么区别？

terminus 这类库在服务内部实现关闭行为，shutdown-check 则从服务外部测试这些行为。

两者可以一起使用：用库注册处理函数、关闭资源，再用 shutdown-check 证明真实的启动命令、信号传递路径、进行中的请求、进程退出和端口关闭能够协同工作。[对比页面](https://shutdown.jscrate.dev/zh/docs/comparison)还介绍了单元测试、脚本和预发布环境测试这几种方式。

## shutdown-check 会发送 SIGINT 吗？

不会。1.0.1 版本只支持 `SIGTERM`，配置了其他信号会被拒绝。这与容器运行时和大多数进程管理器使用的优雅终止方式一致。

## 能同时测试多个请求吗？

可以。将 `workload.concurrent` 设为 1 到 20，并使用 `response-headers` 启动确认。每个请求都必须在发送信号前处于进行中，并以预期的响应结束。

`probe` 启动确认只支持一个请求，因为多个操作共用一个探测路由时，无法分辨是哪一个开始了。

## 为什么 CLI 以退出码 2 退出，却没有 SC 码？

说明测试根本没有运行。退出码 `2` 表示命令行参数错误、JSON 缺失或无效，以及配置校验失败。stderr 中的消息以 `shutdown-check:` 开头。

先根据这条消息修复问题。只有有效的测试开始运行后，才会产生 SC 码。详见[故障排查](https://shutdown.jscrate.dev/zh/docs/troubleshooting#the-cli-exits-with-code-2)。

## 关闭截止时间应该怎么设？

截止时间必须覆盖 `SIGTERM` 之后最慢的正常请求和清理所需的时间，并留出余量。同时，它必须小于平台强制终止进程的截止时间。

在 Kubernetes 中，用 `terminationGracePeriodSeconds` 减去 `preStop` 的耗时。不要靠拉长截止时间来掩盖可能永远挂起的工作，而应为操作和清理设置超时。

## 在 CI 中应该运行什么？

先构建应用，然后运行：

```bash
npx shutdown-check test --json --junit shutdown-result.xml
```

出现关闭问题时，退出码 `1` 会让任务失败。JUnit 报告可以和其他测试结果一起发布。请使用专用端口和隔离的测试数据。

## 相关内容

- [快速开始](https://shutdown.jscrate.dev/zh/docs/quick-start)
- [配置参考](https://shutdown.jscrate.dev/zh/docs/configuration)
- [诊断码](https://shutdown.jscrate.dev/zh/docs/codes)
- [兼容性与限制](https://shutdown.jscrate.dev/zh/docs/compatibility)
- [故障排查](https://shutdown.jscrate.dev/zh/docs/troubleshooting)
