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

使用Puppeteer批量生成PDF时出现超时及应用卡顿问题的解决方案咨询

Puppeteer批量生成PDF时出现超时及应用卡顿问题的解决方案咨询

看起来你在批量生成PDF时遇到了实打实的麻烦——数据量一上来(10k-20k条)就卡得动不了,还弹出WS端点超时的错误,这确实够头疼的!咱们先拆解下问题出在哪,再一步步优化你的代码。

先说说你当前代码的核心问题

  1. 无节制的浏览器实例创建:你每次循环都调用launch()新建一个Puppeteer浏览器,10k条数据就会瞬间启动10k个浏览器进程,系统CPU、内存直接爆掉,超时就是因为浏览器根本启动不过来。
  2. forEach+async/await的错误用法:forEach不支持异步等待,你的代码会瞬间把所有异步任务扔出去,并发量直接拉满,资源彻底耗尽。
  3. 低效的数据库操作:每条数据都单独插一次数据库,频繁的连接和查询也会拖慢整个流程。

针对性优化方案

下面是调整后的代码和关键优化点,咱们一步步来:

1. 复用Puppeteer浏览器实例,不要每次都新建

先修改你的generatepdf.js,让它接受外部传入的浏览器实例,只在循环里新建页面(用完就关页面,别关浏览器):

const pdfrouter = async (data, browser) => {
  try {
    const templateHtml = fs.readFileSync(
      join(process.cwd(), "./index.html"),
      "utf8"
    );
    // 可以把模板编译移到函数外面,缓存起来,不用每次都编译
    const template = Handlebars.compile(templateHtml);
    const html = template(data);
    
    const options = {
      width: '1230px',
      headerTemplate: "<p></p>",
      footerTemplate: "<p></p>",
      displayHeaderFooter: false,
      margin: {
        top: "10px",
        bottom: "30px"
      },
      printBackground: true,
    };

    // 复用已有的浏览器,新建页面
    const page = await browser.newPage();
    console.log(`开始处理员工ID: ${data.employeeid}`);
    
    // 用setContent代替data URI,更高效且避免长度限制
    await page.setContent(html, { waitUntil: 'networkidle0' });

    const pdfBuffer = await page.pdf(options);
    await page.close(); // 只关闭页面,保留浏览器
    console.log(`员工ID ${data.employeeid}的PDF生成完成`);

    return {
      employeeid: data.employeeid.toString(),
      base64str: pdfBuffer.toString('base64'),
      testid: data.testid
    }
  } catch (err) {
    console.error(`生成员工ID ${data.employeeid}的PDF出错:`, err);
    throw err; // 抛出错误,方便后续统一处理
  }
}

2. 控制并发数,避免资源耗尽

用p-limit库(需要先安装:npm i p-limit)来限制同时运行的PDF生成任务数,根据你的服务器配置调整,比如先设为10,再慢慢试出最佳值。同时修改createPDF函数,批量处理数据库插入:

import { testres1 } from './getdata.js'
import { pdfrouter } from "./generatepdf.js";
import { pool } from "./dbconn.js";
import pLimit from 'p-limit';
import format from 'pg-format'; // 用于批量插入,需安装:npm i pg-format

// 控制并发数,根据服务器性能调整,比如10
const limit = pLimit(10);

async function createPDF() {
  const batchInsertQuery = 'INSERT INTO base64staging(filename, base64str, testid) VALUES %L';

  try {
    const result = await testres1(pool);
    if (!result) {
      console.log('没有需要处理的数据');
      return [];
    }
    const dataList = JSON.parse(result);
    console.log(`共需处理 ${dataList.length} 条记录`);

    // 只启动一次浏览器,全程复用
    const browser = await launch({
      args: [
        '--no-sandbox',
        '--disable-dev-shm-usage' // 解决Linux下/dev/shm内存不足的问题,常见超时原因
      ],
      headless: "new"
    });

    try {
      // 用p-limit包装每个PDF生成任务,控制并发
      const pdfPromises = dataList.map(data => 
        limit(async () => pdfrouter(data, browser))
      );

      // 等待所有任务完成(不管成功失败)
      const pdfResults = await Promise.allSettled(pdfPromises);
      console.log(`PDF生成完成,共处理 ${pdfResults.length} 条记录`);

      // 筛选成功的结果,准备批量插入
      const successfulEntries = pdfResults
        .filter(res => res.status === 'fulfilled')
        .map(res => res.value);

      console.log(`准备插入 ${successfulEntries.length} 条成功生成的记录`);

      // 批量插入数据库,减少查询次数
      const insertQuery = format(batchInsertQuery, successfulEntries.map(entry => [
        entry.employeeid,
        entry.base64str,
        entry.testid
      ]));
      await pool.query(insertQuery);

      return successfulEntries;
    } finally {
      // 不管成功失败,最后都关闭浏览器
      await browser.close();
      console.log('浏览器已关闭');
    }
  } catch (err) {
    console.error('createPDF流程出错:', err);
    throw err;
  }
}

const testres2 = await createPDF();

3. 额外的优化建议

  • 缓存Handlebars模板:把const template = Handlebars.compile(templateHtml)移到pdfrouter函数外面,只编译一次,提升效率。
  • 分批次处理超大数据:如果数据量超过5万,可以分成多个批次(比如每批1000条),处理完一批再处理下一批,避免内存占用过高。
  • 监控系统资源:用top或htop查看CPU、内存使用情况,调整并发数,找到服务器能承受的最大值。
  • 优化模板加载:如果模板内容固定,可以预加载到内存,不要每次都读文件。

为什么这些优化能解决你的问题?

  • 复用浏览器实例:把浏览器启动的开销从10k次降到1次,彻底避免了浏览器启动超时的问题。
  • 控制并发:不让系统同时处理过多任务,CPU和内存不会被瞬间占满,应用也就不会卡顿了。
  • 批量插入:减少数据库的连接和查询次数,提升整体流程的速度。

备注:内容来源于stack exchange,提问作者Roger Holland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 08:53:08