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

Puppeteer:新建Browser与Page哪个更耗内存?Heroku资源优化咨询

复用Puppeteer Browser实例解决Heroku Dyno内存问题

绝对应该把Browser设为全局变量复用,这正是解决你当前内存溢出问题的核心方案。

为什么当前方案会炸内存?

每次处理POST请求都启动一个全新的Browser实例,代价极高——每个Chromium Browser本身就会占用几十MB甚至上百MB的内存(包括主进程、渲染进程、V8引擎等)。在512MB的免费Heroku Dyno上,同时处理十几二十个请求就可能把内存占满,更别说100次请求了,Dyno自然会因为内存超限被终止。

而单个Page实例的内存开销要小得多,复用同一个Browser创建/销毁Page,能大幅降低内存占用,同时还能节省每次启动Chromium的时间,提升API响应速度。

优化后的完整代码示例

const express = require('express');
const puppeteer = require('puppeteer');
const app = express();

// 全局Browser实例,仅启动一次
let browser;

// 启动Express前先初始化Browser
(async () => {
  try {
    browser = await puppeteer.launch({
      args: [ '--no-sandbox', '--disable-setuid-sandbox', ],
      // 可选:启用新版无头模式,进一步节省内存
      headless: 'new'
    });
    console.log('Puppeteer Browser initialized successfully');
  } catch (err) {
    console.error('Failed to initialize Puppeteer Browser:', err);
    process.exit(1); // 初始化失败直接退出,避免服务异常运行
  }
})();

// 解析JSON请求体
app.use(express.json());

app.post('/scrape-images', async (req, res) => {
  let page;
  const { url } = req.body;

  if (!url) {
    return res.status(400).send({ errormsg: 'URL parameter is required' });
  }

  try {
    // 检查Browser是否正常运行,意外崩溃则重启
    if (!browser || !browser.isConnected()) {
      browser = await puppeteer.launch({
        args: [ '--no-sandbox', '--disable-setuid-sandbox', ],
        headless: 'new'
      });
    }

    page = await browser.newPage();
    
    // 优化:拦截不必要的资源,减少内存占用
    await page.setRequestInterception(true);
    page.on('request', (req) => {
      const blockedResources = ['stylesheet', 'script', 'image', 'media', 'font'];
      if (blockedResources.includes(req.resourceType())) {
        req.abort();
      } else {
        req.continue();
      }
    });

    await page.setViewport({ width: 1200, height: 1200 });
    await page.goto(url, { waitUntil: 'load', timeout: 0 });
    
    const imgs = await page.$$eval('.img-wrapper img[src]', imgs => 
      imgs.map(img => img.getAttribute('src'))
    );

    res.send({ imageList: imgs });
  } catch (e) {
    console.error('Scraping failed:', e);
    res.status(500).send({ errormsg: "Something went wrong with the scrapper" });
  } finally {
    // 确保Page被关闭,释放内存
    if (page) {
      await page.close().catch(err => console.error('Failed to close page:', err));
    }
  }
});

// 可选:健康检查路由,用于Heroku监控
app.get('/health', (req, res) => {
  res.sendStatus(browser && browser.isConnected() ? 200 : 503);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

额外优化建议

  • 限制并发Page数量:即使复用Browser,同时打开太多Page也会消耗内存。可以用p-limit之类的库限制并发请求数,比如设置最多5个并发Page。
  • 清理Page残留数据:在每次关闭Page前,可以调用await page.deleteCookie()或清理本地存储,避免残留数据占用内存。
  • 监控内存使用:可以在代码中加入内存监控日志,比如console.log(process.memoryUsage()),方便排查内存异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:49:02