Puppeteer大规模PDF生成致VM资源耗尽崩溃问题求助
Puppeteer大规模PDF生成导致GCP虚拟机资源耗尽崩溃的优化方案
我在测试Puppeteer的大规模PDF生成能力时,执行每秒5次请求、持续1分钟的压力测试,发现Google Cloud e2-highcpu-4(4核4GB内存、Ubuntu系统)虚拟机因资源耗尽崩溃。相关代码如下:
generatePdf函数
async generatePdf({ url, displayHeaderFooter, headerContent, footerContent, width, height, marginTop, marginRight, marginBottom, marginLeft}: IPDFRepositoryDTO): Promise<Buffer> { if (!global.browser) { await launchPuppeteer(); } const page = await global.browser.newPage(); await page.goto(url, { waitUntil: 'networkidle0', }); const isDisplayHeaderFooter = displayHeaderFooter != undefined ? displayHeaderFooter : true; const pageOptions = { printBackground: true, format: 'Letter', displayHeaderFooter: isDisplayHeaderFooter, headerTemplate: headerContent, footerTemplate: footerContent, width, height, margin: { top: marginTop || '80px', bottom: marginBottom || '80px', left: marginLeft || '20px', right: marginRight || '20px', }, } as PDFOptions; const pdf = await page.pdf(pageOptions); await page.close(); return pdf;}
launchPuppeteer函数
export const launchPuppeteer = async () => { global.browser = await launch({ args: [ // '--no-sandbox', // '--disable-setuid-sandbox', // '--disable-dev-shm-usage', // '--disable-accelerated-2d-canvas', // '--no-first-run', // '--no-zygote', // '--single-process', // '--disable-gpu', '--no-sandbox', '--disable-accelerated-2d-canvas', '--no-zygote', '--single-process', '--disable-gpu', '--disable-canvas-aa', // Disable antialiasing on 2d canvas '--disable-2d-canvas-clip-aa', // Disable antialiasing on 2d canvas clips '--disable-gl-drawing-for-tests', // BEST OPTION EVER! Disables GL drawing operations which produce pixel output. With this the GL output will not be correct but tests will run faster. '--disable-dev-shm-usage', '--use-gl=swiftshader', // better cpu usage with --use-gl=desktop rather than --use-gl=swiftshader, still needs more testing. '--enable-webgl', '--hide-scrollbars', '--mute-audio', '--no-first-run', '--disable-infobars', '--disable-breakpad', //'--ignore-gpu-blacklist', // '--window-size=1280,1024', // see defaultViewport // '--user-data-dir=./chromeData', // created in index.js, guess cache folder ends up inside too. '--disable-setuid-sandbox' ], }); console.log('browser ready'); };
核心问题分析
当前代码无并发控制,高请求量下瞬间创建大量页面,导致CPU和内存被完全占用;同时Puppeteer启动参数存在冗余,部分参数反而限制了资源利用效率,最终引发虚拟机崩溃。
优化方案
1. 限制并发页面数量
用队列机制控制同时处理的PDF生成任务数,避免瞬间耗尽资源。建议并发数与CPU核心数匹配(4核机器设为4):
// 引入p-queue实现队列管理(也可自行实现简单队列) const PQueue = require('p-queue'); const pdfQueue = new PQueue({ concurrency: 4 }); async generatePdf(params) { return pdfQueue.add(async () => { if (!global.browser) await launchPuppeteer(); const page = await global.browser.newPage(); try { await page.goto(params.url, { waitUntil: 'networkidle2' }); const isDisplayHeaderFooter = params.displayHeaderFooter != undefined ? params.displayHeaderFooter : true; const pageOptions = { printBackground: true, format: 'Letter', displayHeaderFooter: isDisplayHeaderFooter, headerTemplate: params.headerContent, footerTemplate: params.footerContent, width: params.width, height: params.height, margin: { top: params.marginTop || '80px', bottom: params.marginBottom || '80px', left: params.marginLeft || '20px', right: params.marginRight || '20px', }, } as PDFOptions; const pdf = await page.pdf(pageOptions); return pdf; } catch (err) { console.error('PDF生成失败:', err); throw err; } finally { // 确保页面无论成功失败都关闭,避免内存泄漏 await page.close().catch(closeErr => console.error('页面关闭失败:', closeErr)); } }); }
2. 优化Puppeteer启动参数
移除冗余参数,启用新版无头模式,降低资源占用:
export const launchPuppeteer = async () => { global.browser = await launch({ args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', // 解决虚拟机共享内存不足问题 '--disable-accelerated-2d-canvas', '--disable-gpu', // 虚拟机无GPU,直接禁用 '--disable-canvas-aa', // 禁用画布抗锯齿,减少CPU消耗 '--disable-2d-canvas-clip-aa', '--disable-gl-drawing-for-tests', // 禁用GL绘图,提升速度 '--hide-scrollbars', '--mute-audio', '--no-first-run', '--disable-infobars', '--disable-breakpad' ], headless: 'new', // 使用新版无头模式,资源占用更低 defaultViewport: { width: 1280, height: 720 }, // 设置默认视口,避免自适应消耗资源 }); console.log('browser ready'); };
3. 调整页面加载策略
networkidle0等待所有网络连接断开,高并发下会延长等待时间。改为networkidle2(允许最多2个活跃连接)或load(页面加载完成即停止),根据页面实际情况选择:
await page.goto(params.url, { waitUntil: 'networkidle2' });
4. 可选:虚拟机资源升级
如果业务需求的并发量确实较高,可考虑升级到e2-highcpu-8(8核8GB内存)虚拟机,或采用容器化部署配合自动扩容,应对峰值请求。
内容的提问来源于stack exchange,提问作者Eder Aparecido
相关产品推荐
相关产品推荐

