Express.js中批量异步请求致网站冻结的优化方案咨询
问题分析与解决方案
核心问题:网站冻结的原因
你遇到的网站冻结,本质是一次性发起了过量的并发网络请求。Node.js是单线程事件循环模型,虽然网络请求是异步的,但短时间内创建数百/上千个Promise并通过Promise.all同时执行,会占用大量系统资源(比如文件描述符),同时目标服务器可能因为请求过载返回缓慢,导致事件循环被大量回调任务堆积,其他用户请求或网站操作无法及时得到处理,表现为整个网站“冻结”。
优化方案:控制请求并发数
要在等待所有请求完成的前提下避免冻结,核心是限制同时发起的请求数量,分批处理请求,而不是一次性全部发起。
方案1:手动实现分批处理
不需要额外依赖包,自己写简单的分批逻辑:
// module.js // 假设你的单个请求函数是makeRequest async function makeRequest(options) { console.log('start'); return new Promise((resolve, reject) => { // 这里是你的request请求逻辑 request(options, (err, res, body) => { if (err) reject(err); else resolve(body); }); }); } // 分批处理请求 async function fetchDataInBatches(requestOptionsList, batchSize = 5) { const allResults = []; const total = requestOptionsList.length; for (let i = 0; i < total; i += batchSize) { // 截取当前批次的请求配置 const batch = requestOptionsList.slice(i, i + batchSize); // 等待当前批次所有请求完成 const batchResults = await Promise.all(batch.map(opt => makeRequest(opt))); allResults.push(...batchResults); } return allResults; } module.exports = { fetchDataInBatches };
然后在main.js中调用:
// main.js app.post('/mypostPath', async (req, res) => { try { // 假设你有一个请求配置列表requestList const data = await fetchDataInBatches(requestList, 5); // 处理数据后返回 res.send(data); } catch (err) { res.status(500).send('请求处理失败'); } });
通过batchSize控制同时发起的请求数(比如设为5-10,根据目标服务器的承受能力调整),避免一次性占用过多资源。
方案2:使用现成的并发控制工具包
如果不想自己写逻辑,可以用p-limit这类轻量包,它专门用于限制Promise并发数:
// module.js const pLimit = require('p-limit'); const limit = pLimit(5); // 限制同时5个请求 async function fetchData(requestOptionsList) { const tasks = requestOptionsList.map(opt => limit(() => makeRequest(opt))); return Promise.all(tasks); }
关于module.js中直接调用res.send()的问题
可以调用,但强烈不推荐:
- 这样会把数据获取的业务逻辑和HTTP响应耦合在一起,导致module.js无法在其他场景复用(比如定时任务、其他接口)。
- 违反单一职责原则:module.js应该只负责数据获取,HTTP响应的处理应该交给路由层(main.js)来完成。
替代await的方案
await本身就是Promise的语法糖,替代方案本质还是使用Promise的链式调用,但可读性不如await:
// main.js中不用await的写法 app.post('/mypostPath', (req, res) => { fetchDataInBatches(requestList) .then(data => { // 处理数据 res.send(data); }) .catch(err => { res.status(500).send(err.message); }); });
但这种写法并没有解决核心的并发问题,只是改变了代码风格,还是要配合前面的并发控制方案才能解决冻结问题。
内容的提问来源于stack exchange,提问作者unbaym
相关产品推荐
相关产品推荐

