一个预渲染的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。它很棒。如果有多个容器监听同一个域名,它会自动[负载均衡](https://github.com/nginx-proxy/nginx-proxy/tree/main/