You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Puppeteer/Chromium如何处理高内存页面崩溃与服务器挂起问题?

这个问题我之前处理过类似场景,大页面导致Chromium内存过载进而让服务器挂起确实很棘手,咱们从内存限制、错误处理优化、额外性能调优三个方面来解决:

一、用Chromium启动参数限制内存占用

直接在puppeteer.launchargs里添加以下参数,从根源上限制内存使用:

  • --max-old-space-size=512:限制V8引擎的堆内存为512MB(可根据服务器配置调整,比如256或1024),避免单个页面疯狂占用内存
  • --disable-dev-shm-usage:很多Linux环境下/dev/shm空间很小,禁用后改用临时文件存储,能避免因共享内存不足导致的崩溃
  • --memory-pressure-offer-increment=10:调整内存压力触发阈值,让Chromium更早启动内存回收机制
  • 可选:--js-flags="--expose-gc --gc-interval=1000":暴露垃圾回收接口并设置定时GC,配合代码手动触发效果更好(不过需要额外调用页面的GC方法)
二、优化错误处理逻辑

你的原代码存在几个风险点,我帮你调整:

  1. 确保页面必关闭:用try-finally块替代单纯的catch,不管页面加载成功/失败/崩溃,都要关闭页面,避免僵尸进程占用内存
  2. 处理页面崩溃场景:监听page.crash事件,提前捕获崩溃并清理资源
  3. 自定义导航超时:把page.goto的超时时间改短(比如10秒),快速失败,不要等默认的30秒浪费资源
  4. 避免未定义的page调用:catch块里直接调用page.close()可能报错,因为如果page.newPage()失败,page是undefined

修改后的核心代码示例:

const puppeteer = require('puppeteer');
const path = require('path');

(async () => {
  // 带内存限制参数启动Chromium
  const browser = await puppeteer.launch({
    args: [
      '--max-old-space-size=512',
      '--disable-dev-shm-usage',
      '--memory-pressure-offer-increment=10'
    ],
    headless: 'new' // 启用新版无头模式,更低内存占用
  });

  for (let id of ids) {
    let page;
    try {
      page = await browser.newPage();

      // 监听页面崩溃事件,及时清理
      page.on('crash', async () => {
        console.log(`⚠️ Page ${id} crashed due to memory overload`);
        if (page) await page.close().catch(() => {}); // 静默处理关闭失败
      });

      // 自定义导航超时,快速失败
      await page.goto(url + id, { timeout: 10000 });

      // 可选:拦截大资源,减少内存占用
      await page.setRequestInterception(true);
      page.on('request', (req) => {
        const resourceType = req.resourceType();
        // 阻止加载图片、视频等非必要大资源(根据业务调整)
        if (['image', 'media', 'font'].includes(resourceType)) {
          req.abort();
        } else {
          req.continue();
        }
      });

      await page.waitFor(5000);
      await page.screenshot({ 
        path: path.join(__dirname, "../public/images/screenshots/" + id + ".png"), 
        clip: { x: 10, y: 70, width: 780, height: 470} 
      });
    } catch (error) {
      console.log('❌ Exception', id, error.message);
    } finally {
      // 确保页面关闭,处理关闭失败的情况
      if (page) {
        await page.close().catch(err => console.log(`Failed to close page ${id}:`, err.message));
      }
    }
  };

  await browser.close();
})();
三、额外的性能调优建议
  • 控制并发数:如果ids数量很大,不要一次性循环创建页面,用p-limit库限制并发数(比如同时最多3个页面),避免内存瞬间暴涨
  • 定期重启浏览器:每处理N个任务后重启一次浏览器,避免长期运行导致的内存泄漏累积
  • 清理临时文件:确保screenshot的目录有定期清理机制,避免磁盘占满影响服务器

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

火山引擎 最新活动