为何Node.js CPU与IO密集任务负载测试下/fast接口响应时长远近一致?
Node.js 并行负载测试疑问:/fast接口响应时间为何无差异?
问题描述
已知Node.js擅长处理网络与非阻塞I/O场景,我创建了三个API接口:
- 响应快速的
/fast接口; - CPU密集型的
/cpu接口(通过while循环占用CPU资源5秒); - I/O密集型的
/io接口(发起外部网络请求)。
我进行了两组并行负载测试:
- 同时调用
/fast与/cpu接口; - 同时调用
/fast与/io接口。
测试后发现/fast接口的平均响应时间在两组测试中几乎相同,但我原本预期I/O密集任务组的/fast接口响应时间会更短,特此询问原因。
相关代码
app.get("/fast", (req, res) => { res.send("I am fast"); }); app.get("/cpu", (req, res) => { const start = Date.now(); while (Date.now() - start < 5000) { } res.send("I took 5 seconds"); }); app.get("/io", (req, res) => { axios.get('https://reqres.in/api/users?page=2') .then(function (response) { res.send("Network call success"); }) });
测试信息
- 测试结果截图:
- 图1:IO密集型API负载测试结果
- 图2:CPU密集型API负载测试结果
- 测试说明:
/fast与/cpu接口并行执行,/fast与/io接口并行执行 - 计算机配置:AMD Ryzen 5 3400G with Radeon Vega Graphics 3.70 GHz,内存8GB
原因解析
1. Node.js单线程事件循环的核心特性
Node.js基于单线程事件循环模型,所有JavaScript代码(包括API回调)都在主线程中执行。异步I/O操作(如网络请求)会被交给操作系统的异步线程池处理,主线程不会被阻塞,可继续处理其他任务。
2. CPU密集组的请求处理逻辑
当/fast与/cpu请求同时到达时,它们会进入事件队列等待主线程处理:
- 如果
/fast的回调先被执行:会立即响应,耗时可忽略; - 如果
/cpu的回调先被执行:while循环是同步阻塞操作,会完全占用主线程5秒,/fast的请求必须等循环结束才能被处理,耗时约5秒。
在多次重复测试中,两种情况出现的概率基本均等,因此/fast的平均响应时间会接近2.5秒左右。
3. I/O密集组的请求处理逻辑
当/fast与/io请求同时到达时:
- 如果
/fast的回调先被执行:立即响应; - 如果
/io的回调先被执行:主线程执行到axios.get时,会将网络请求交给异步线程池处理,主线程立即释放,回到事件循环处理/fast的请求,因此/fast仍能快速响应,耗时可忽略。
4. 两组测试结果接近的可能原因
你观察到两组/fast响应时间几乎相同,大概率是以下情况导致:
- 测试样本量不足:如果测试次数太少,CPU组中
/fast先被处理的次数占比过高,拉低了平均响应时间,使其接近IO组; - 请求发起时机存在偏差:JMeter在发起并行请求时,可能存在微小的时间差,导致
/fast请求总是先到达Node.js,即使在CPU组中也能快速响应; - Node.js进程调度的特殊情况:若你无意中启用了多进程模式(如cluster模块),
/cpu请求会被分配到单独的进程,不会阻塞处理/fast请求的进程,从而使两组响应时间一致。
内容的提问来源于stack exchange,提问作者Subrahmanyam Pampana
相关产品推荐
相关产品推荐

