Node.js使用loadtest压测Express服务时出现Error: read ENOTCONN问题
问题分析与解决
错误根源
你的Express服务器里的longComputation()是同步CPU密集型任务,Node.js基于单线程事件循环模型运行,一旦这个函数执行,会完全阻塞事件循环,无法处理任何网络IO(包括新连接建立、已连接socket的读写操作)。当你用loadtest -n 10 -c 10发起10个并发请求时,所有请求会排队等待第一个请求的计算完成,期间客户端(loadtest)的连接长时间得不到响应,被底层网络层判定为无效连接,进而抛出ENOTCONN(尝试对未连接的socket执行读操作)错误。
解决方案:将CPU密集型任务异步化
要解决这个问题,必须把同步阻塞的计算任务移出主线程,避免卡住事件循环。推荐使用Node.js原生的worker_threads模块,把计算任务放到独立的工作线程中执行。
修改后的代码示例
- 先创建独立的工作线程文件
compute.worker.js:
// compute.worker.js const { parentPort } = require('worker_threads'); parentPort.on('message', () => { let sum = 0; for(let i=0; i < 1e9; i++){ sum += i; } parentPort.postMessage(sum); });
- 修改主Express服务器代码:
const express = require('express'); const { Worker } = require('worker_threads'); const app = express(); app.get('/one', (req, res) => { console.log('received one request'); // 创建工作线程处理计算任务 const worker = new Worker('./compute.worker.js'); worker.postMessage('start'); // 接收工作线程返回的计算结果 worker.on('message', (sum) => { res.send({sum: sum}); worker.terminate(); // 计算完成后销毁线程释放资源 }); // 捕获工作线程异常 worker.on('error', (err) => { res.status(500).send({error: err.message}); worker.terminate(); }); }); app.listen(5632, () => { console.log(`Server listening on 5632...`) });
额外优化建议
- 可以搭建工作线程池,避免每次请求都创建新线程,减少线程创建销毁的资源开销。
- 如果计算任务可拆分,也可以用
setImmediate分批次执行循环,每次只处理一部分计算后让出事件循环处理IO,但这种方式总执行时间会更长,仅适合对耗时要求不高的场景。
测试验证
修改完成后,再次执行压测命令:
loadtest -n 10 -c 10 http://localhost:5632/one
此时服务器可以正常处理并发请求,不会再出现ENOTCONN错误。
内容的提问来源于stack exchange,提问作者Pooja Seethur
相关产品推荐
相关产品推荐

