一个预渲染的Next.js网站到底能承受多少流量?
我经常说“一个预渲染的网站可以轻松应对数百个并发用户”,因为,嗯,我从未见过它失败。
但它真正能承受多少呢?我的网站能否应对来自像Hacker News首页这样的流量激增?它与服务器端渲染相比如何?是否值得为了避免SSR而大费周章?
我四处寻找Next.js性能的硬数据,但令人惊讶的是,很难找到可靠的数据。于是,我在自己的网站上运行了一些测试,结果出乎意料。就在此时,我关于Google Translate干扰React的文章登上了Hacker News的首页。
就在我发现(剧透警告)我的网站可能无法应对这种情况之后。
预渲染网站与VPS
我首先想知道的是,如果我的网站因为登上Hacker News首页而迎来大量访问,它能否应对。
为了测试这一点,我写了一个基本的k6负载测试脚本,以测量我的服务器每秒能处理的最大请求数。脚本尽可能简单;它会尽可能多次请求一个页面,同时等待每个请求完成。你可以在附录中找到完整的测试设置。
图片:

运行这个脚本,在TransIP的X4 BladeVPS上,一个单独的Next.js实例为我的完全预渲染的首页每秒可以处理193个请求,但只有63%的请求在500毫秒内响应。
需要强调的是:测试中的每个请求只是对预渲染的Next.js页面的一次调用。没有请求任何资源。一个真实的访问者需要60多个额外的请求。这意味着仅仅三个同时访问的用户就可能将服务器推向极限。
这比我预期的要低得多。
访问的解剖
为了理解为什么这个网站需要60多个请求,让我们看看当有人访问我的首页时发生的所有请求:
- DNS查找和SSL握手。
- 初始请求(1: 20.3 kB):浏览器请求HTML文档。
- CSS(1: 1.6 kB):浏览器解析HTML并请求必要的CSS文件。
此时页面已经完全样式化并可见,用户可以在JavaScript加载之前与大多数元素交互。 主要缺少的是:
- 首屏图片(5: 6.8 kB):浏览器请求首屏图片。
通常只有几张首屏图片,此时的总大小通常在50 kB到100 kB之间。这时访问者开始看到一个完整的页面。 由于大多数交互元素是原生实现的(使用平台),用户也可以开始与页面交互(例如导航)。
其余的请求是“延迟的”;它们是低优先级的,不会阻塞渲染。这包括大多数请求:
- 分析(1: 2.5 kB):浏览器请求Plausible脚本。
- JavaScript(13: 176.8 kB):浏览器请求主页面所需的JavaScript。
- 懒加载图片(1: 22.8 kB):浏览器请求首屏部分懒加载的图片。
- 占位符数据(17: 11.9 kB):浏览器请求首屏下方图片的占位符数据。
- 图标(2: 52.2 kB):浏览器请求各种尺寸的图标。
- 预加载资源(22: 136.1 kB):浏览器预加载潜在导航的资源(以使它们即时加载)。
最终总共有63个请求和434.5 kB。
这表明Next.js引入了许多额外的请求和流量,相比于更标准的服务器渲染网站或非Next.js的静态网站。虽然这让我作为开发者可以完全控制用户体验(UX)和出色的开发者体验(DX),但也意味着用户和服务器必须处理更多的流量。话又说回来,我不认为<500 kB对于现代网站来说太多,尤其是页面在加载前100 kB后已经可用。
React Server Components(RSC)可以通过移除部分客户端JavaScript来略微改善这一点。不幸的是,该平台还不太成熟,仍然存在许多问题。我愿意为了最佳性能而处理这些问题,但主要障碍仍然是它缺乏CSS-in-JS支持。一旦这个问题解决(即PigmentCSS发布),我会立即采用。
扩展
在看到我的VPS表现不佳后,我尝试扩展我的Docker容器(docker compose up -d --scale main=2 main)。现在,每个CPU核心(总共2个核心)都有一个Node.js进程处理请求,而不是一个容器处理所有请求。
图片:

标题:扩展网站的容器
旁注:
旁注:
我在Docker容器前使用nginx-proxy。它很棒。如果有多个容器监听同一个域名,它会自动负载均衡。
这几乎没有帮助。VPS现在可以处理每秒275个请求(↑42.49%),但仍然只有76%的请求在500毫秒内响应。仍然无法应对大规模的“流量激增”。我真的不希望我的网站宕机。
我还测试了其他文件,以了解服务器如何处理额外的60多个请求。以下是我的发现:
旁注:
旁注:
一个有趣的现象;扩展只导致了首页RPS↑42.49%的增长,因为Next.js已经为GZIP压缩做了基本的多线程处理。这意味着原始的单个容器在扩展之前已经从第二个CPU核心中略微受益。
意外的流量激增
就在我发现我的服务器可能无法应对大规模流量激增的第二天,我的文章关于Google Translate崩溃React(和其他Web应用)登上了Hacker News的首页。
图片:

标题:帖子显示在14日时是一小时前发布的,而实际发布日期是两天前。
这完全出乎意料,因为我两天前才发布了这篇文章。原来Hacker News可以将旧帖子直接推上首页,给它们第二次机会。我很高兴我的文章(我花了很多时间写的)得到了关注,但在我意识到我的网站可能无法应对的情况下,这让我倍感压力。
幸运的是,流量并没有我预期的Hacker News首页那么激烈,我的VPS挺住了。但这让我好奇,流量激增到底能有多糟糕?这将是未来文章的主题,同时我也会分享我的经验数据。
寻找替代方案
对VPS的性能感到沮丧后,我开始寻找更好的解决方案。
Cloudflare
简单的答案是在我的服务器前加上Cloudflare,让它缓存所有的静态内容。我以前用过这种设置;当我运行WoWAnalyzer时,一台服务器加上Cloudflare轻松处理了一个月内超过550,000个独立访问者。
这是一个不错的提议;有了Cloudflare,我的服务器只需要处理初始请求,而Cloudflare会处理其他60个请求。由于我的VPS已经可以每秒处理200个访问者,这意味着问题解决了。
但不幸的是,Cloudflare对我来说不是一个选项。我关心(你们的)隐私,我不希望Cloudflare坐在我的访问者和我的网站之间,记录每个请求并可能跟踪用户。此外,在当前的政治气候下,还有其他政治问题。
我想坚持只使用欧盟的服务。
Vercel
另一个明显的选择是Vercel(或欧盟的等效服务)。但说实话,这对我来说听起来像是噩梦。它不便宜,按站点定价意味着我要么永远锁定,要么在停止项目时不得不拆除一切,而且它有不可预测的限制,对每一个能想到的东西都会额外收费。这让病毒式传播从技术挑战变成了担心下一张账单的总金额。我当前的设置是固定成本,我真的很喜欢这一点。
旁注:
旁注:
我确实为Plausible付费,它也有基于使用量的定价。但我不介意为Plausible付费有三个原因:他们的定价步骤是可预测和合理的,他们不会对偶尔的流量激增收费,而且它不是必需的,我可以随时放弃。我可能未来会尝试自托管以节省一些钱,但上次我用类似的
