You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:27:42