# Node.js HTTP 响应缓存示例

一个只使用 Node.js 内置模块的常驻 HTTP 服务，演示内存缓存命中、过期后返回旧内容并后台刷新，以及主动清除缓存。

需要 Node.js 22 或更高版本，本地验证使用 Node.js 26.12.0。没有第三方依赖，不需要安装包。

## 运行

在源码包根目录执行：

```sh
node copy/server.mjs
```

只下载 `server.mjs` 时，在文件所在目录执行 `node server.mjs` 即可。

默认监听本机 `127.0.0.1:3000`，按 Ctrl+C 停止。端口被占用时可以执行 `node copy/server.mjs 3001`，后面的请求改用对应端口。

另开终端请求接口。macOS、Linux 使用 `curl`；Windows PowerShell 使用 `curl.exe`，下文统一写成 `curl`。

```sh
curl -i http://127.0.0.1:3000/data
```

- 第一次等待约一秒，响应头为 `X-Cache: MISS`，正文包含 `version` 和 `generatedAt`。
- 立即再次请求，响应头为 `HIT`，版本不变。
- 等六秒请求，响应头为 `STALE`，先收到旧版本，终端同时打印后台生成日志。
- 再等两秒请求，响应头为 `HIT`，版本更新。

主动清除后再读一次：

```sh
curl -X DELETE http://127.0.0.1:3000/cache
curl -i http://127.0.0.1:3000/data
```

下一次请求为 `MISS`。若要观察完全过期，在新副本生成后连续超过三十五秒不请求，再访问 `/data`。

`FRESH_MS` 控制新鲜期，`STALE_MS` 是新鲜期之后允许使用旧副本的额外时长。当前分别为五秒和三十秒，每次成功生成后重新计时。`X-Cache-Age` 的单位是秒。

## 验证

在源码包根目录执行：

```sh
node examples/verify.mjs
```

脚本启动随机本机端口，通过真实 HTTP 请求检查冷缓存并发去重、HIT、后台刷新、主动清除、旧任务晚到和完整窗口结束后的重新生成。大约需要一分钟；结束后关闭测试服务。所有数据在本机产生，不访问外部接口。

需要保存原始观察记录时，传入一个输出文件路径：

```sh
node examples/verify.mjs verification.json
```

文件记录响应版本、缓存状态和本地耗时。测试中的一秒异步等待模拟数据源延迟，不能当作 CPU 开销或性能基准。

## 实现与迁移边界

`copy/server.mjs` 是完整服务，`generateData()` 是替换成真实数据源的位置。`refreshing` 让并发请求共用一次生成，也用任务身份避免清除后的旧任务恢复缓存。

示例只缓存 `GET /data` 的一份公共结果，没有身份、分页和语言等缓存键维度。扩展到多资源或个人数据前，需要增加对应缓存键、访问授权和容量限制，不能直接把所有请求共用这一份状态。

`DELETE /cache` 只清除当前进程的副本并解除加载任务关联，不会中止已经发出的数据源请求；原来已经等待该任务的请求仍可能收到结果。清除入口仅供本机演示，接入公开服务时需要访问控制。

缓存不持久化，重启清空，多进程不共享。HTTP Server 每次仍然执行，命中跳过的是 `generateData()`。浏览器收到 `Cache-Control: no-store`，服务器自己的内存复用是应用显式行为。

后台任务依赖常驻 Node.js 进程。迁入 Serverless 平台时，需要使用目标平台支持的后台生命周期机制。示例捕获后台错误并保留旧副本；超过旧内容窗口后，生成失败返回 HTTP 500。

文章与逐文件索引：

- [把缓存放在计算之前：从 Workers Cache 到 Node.js 实现](https://shengsheng.fun/2026/11/01/cache-before-compute/)
- [FILES.json](/files/cache-before-compute/kits/node-response-cache/FILES.json)
