# 如何对 React SSR 性能进行基准测试

有用的 SSR 基准测试可以回答一个特定问题：应用程序可以在资源预算内以多少延迟和成本完成多少正确工作？仅每秒请求数这一数字就无法解释太多。

此方法适用于版本、配置文件或 React 运行时。它将 HTTP 测量与浏览器旅程分开，并包含一个 k6 示例以适应固定路线。该示例本身没有建立任何性能结果。

## 定义正确的响应

选择一个有代表性的路由：动态列表、API 或流式页面。修复数据量、依赖关系和认证条件。固定路由可以返回唯一的请求标记来检测陈旧或不正确共享的响应。

验证状态、内容类型和预期正文值。对于动态 SSR，验证渲染是否实际发生。缓存的静态文件和新渲染的页面是不同的工作负载。

## 使环境具有可比性

确定提交、工件、节点、React、操作系统和编译器选项。在给定计算机上，保留相同的 Node 可执行文件和应用程序依赖项。使用生产版本并记录缓存和压缩设置。

为整个团队（包括工作人员和任何代理人）设定预算。解释拓扑：与直接服务器相比，代理后面的两个进程需要上下文。最好使用单独的负载生成器并监视其是否饱和。

## 创建动态夹具路径

在 App Router 测试应用程序中，使用此内容创建 app/bench/page.jsx。标记根据请求而变化，并且动态呈现是明确的。 This example does very little work;在评估业务场景时将其替换为代表性渲染。

为比较的运行时间保留相同的来源。单独的标记并不能证明所有数据的隔离：当用户属于场景的一部分时，添加单独的会话和特定于应用程序的断言。

```jsx
export const dynamic = "force-dynamic";

export default async function Benchmark({ searchParams }) {
  const { id = "missing" } = await searchParams;
  return <main>Benchmark marker: {id}</main>;
}
```

## 向负载生成器添加验证

将此示例保存为 bench.js。它需要一个 /bench 固定路径，将 id 查询参数回显到其 HTML 中。在您的测试应用程序中创建该路线； Next.js 和 PRNext 都不会自动提供它。 BASE_URL 针对您控制的测试实例。

当响应验证失败时，检查阈值会使运行无效。禁用重定向以避免默默地测量不同的页面。这是一个封闭并发的例子：当服务器速度变慢时，每个虚拟用户等待的时间会更长。它不模拟固定的到达率。

```js
import http from "k6/http";
import { check } from "k6";

export const options = {
  vus: 16,
  duration: "60s",
  thresholds: { checks: ["rate==1"], http_req_failed: ["rate==0"] }
};

export default function () {
  const id = `request-${__VU}-${__ITER}`;
  const base = __ENV.BASE_URL || "http://127.0.0.1:3000";
  const response = http.get(`${base}/bench?id=${id}`, { redirects: 0 });
  check(response, {
    "status 200": r => r.status === 200,
    "HTML response": r => String(r.headers["Content-Type"]).includes("text/html"),
    "fresh marker": r => typeof r.body === "string" && r.body.includes(id)
  });
}
```

## 与测量窗口分开进行预热

安装 k6 后，运行单独的预热，然后运行多个测量窗口。交替候选顺序并保留每次运行的输出。该命令使用 POSIX shell 环境语法；调整 PowerShell 中的 BASE_URL 分配。

生成器有自己的网络和 CPU 成本。保持两个候选者的参数相同并记录竞争服务器活动。重复的窗口有助于描述可变性，但不能消除拓扑偏差或使不具有代表性的夹具具有代表性。

```sh
BASE_URL=http://127.0.0.1:3000 k6 run --duration 10s bench.js
BASE_URL=http://127.0.0.1:3000 k6 run --summary-export result-a.json bench.js
```

## 一起读取吞吐量、延迟、CPU 和内存

p95 是采样请求的 95% 以下完成的持续时间。在 k6 中，http_req_duration 不包括 DNS 查找和初始连接建立等阶段。为指标命名而不是称其为速度。

在测量窗口内分别计算每个正确响应的组 CPU 工作：该组的 CPU 增量除以经过验证的响应。 HTTP p95 不是 CPU 消耗。使用所述方法记录组内存和进程树 PSS。在比较性能之前使包含不正确内容的窗口无效。

## 添加浏览器旅程

客户端可以快速接收 HTML 并且仍然等待水合作用。分别测量导航、后退导航和业务交互。检查计数器、会话或表单状态在整个过程中是否保持正确。

在我们的 VPS 比较中，服务器收益并不会转化为所有浏览器测量中的胜利。发布这些差异会使评估更有用。将协议、原始输出和限制与图表放在一起。

[阅读我们在两台 VPS 机器上的 SSR 和导航活动](/zh/blog/prnext-vs-nextjs-vps-benchmarks)

## Sources

[k6: built-in metrics](https://grafana.com/docs/k6/latest/using-k6/metrics/reference/)

[k6: checks](https://grafana.com/docs/k6/latest/using-k6/checks/)

[k6: thresholds](https://grafana.com/docs/k6/latest/using-k6/thresholds/)

[Linux: cgroup v2](https://docs.kernel.org/admin-guide/cgroup-v2.html)

[Node.js: process.memoryUsage()](https://nodejs.org/api/process.html#processmemoryusage)

已发表: 2026-10-30

修改的: 2026-10-30

作者: PRNext

---

语言: zh-Hans

Canonical: https://prnext.dev/zh/blog/benchmark-react-ssr

Index: https://prnext.dev/llms.txt
