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

读取含大量URL的CSV触发Promise内存泄漏告警,求分批处理方案

解决批量检测网站CSP/XFO策略时的内存泄漏与EventEmitter超限问题

你遇到的问题很典型:当用Promise.all一次性启动100个Puppeteer浏览器实例时,每个实例都会给process对象添加多组事件监听器(exit、SIGINT等),超出了Node.js默认的10个监听器上限,同时大量并发的浏览器进程也会快速耗尽内存,触发泄漏告警。

最根本的解决方案是分批处理任务——控制同时运行的检测任务数量,比如每次只跑10个,完成一批后再启动下一批。这样既能避免监听器超限,也能降低内存占用。下面是具体的实现方案:

第一步:编写分批处理工具函数

先封装一个通用的分批执行函数,用来精准控制并发数:

async function batchProcess(items, batchSize, processFn) {
  const results = [];
  // 循环分割数组,逐批处理
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    // 等待当前批所有任务完成后,再处理下一批
    const batchResults = await Promise.all(batch.map(processFn));
    results.push(...batchResults);
  }
  return results;
}

这个函数会把传入的任务数组按指定的batchSize拆分,确保同一时间只有指定数量的任务在运行,从根源上控制资源占用。

第二步:改造readCSV函数

修改原有的readCSV逻辑,不再一次性启动所有任务,而是用分批函数处理,同时过滤掉表头和无效空行:

function readCSV(csv) {
  const lines = csv.split("\n").filter(line => line.trim() !== ""); // 过滤空行
  const headers = lines[0].split(",");
  const dataLines = lines.slice(1); // 跳过表头,只处理数据行

  // 调用分批处理,批大小设为10(可根据机器性能调整,比如8或15)
  batchProcess(dataLines, 10, async (line) => {
    const currentline = line.split(",");
    const url = `https://www.${currentline[1]}`;
    console.log(`Processing site: ${url}`);
    return check_page(url);
  })
  .then(() => console.log('All site checks completed successfully'))
  .catch(err => console.error('Error during batch processing:', err));
}

第三步:优化check_page函数

原函数里用XMLHttpRequest获取请求头是多余的,Puppeteer的page.goto会直接返回Response对象,通过它就能拿到响应头,还能避免额外引入第三方模块:

async function check_page(web_page) {
  let browser;
  try {
    browser = await puppeteer.launch();
    const page = await browser.newPage();
    // 捕获页面加载后的响应对象
    const response = await page.goto(web_page, { waitUntil: 'domcontentloaded' });
    const mainUrl = page.mainFrame().url();
    console.log(`Checking headers for: ${mainUrl}`);

    // 直接从response提取响应头,转成小写键名的映射
    const headers = response.headers();
    const headerMap = Object.fromEntries(
      Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value])
    );

    // 这里可以添加CSP/XFO的检测逻辑,比如:
    // const cspPolicy = headerMap['content-security-policy'];
    // const xfoPolicy = headerMap['x-frame-options'];
  } catch (err) {
    console.error(`Failed to check ${web_page}:`, err.message);
  } finally {
    // 确保浏览器实例被关闭,即使任务出错也不会残留进程
    if (browser) await browser.close();
  }
}

额外说明:临时绕过监听器告警(不推荐)

如果你只是想临时跳过告警而不调整并发逻辑,可以在代码开头添加:

process.setMaxListeners(0); // 0表示取消监听器数量限制

但这只是治标不治本,大量并发的浏览器实例依然会占用极高内存,甚至导致进程崩溃,所以优先推荐分批处理方案。


内容的提问来源于stack exchange,提问作者Fra96

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 20:42:51