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

本地AWS Lambda中Puppeteer抛出Target closed错误求助

AWS Lambda上NodeJS+Puppeteer转HTML为PDF频繁故障排查与解决

实现代码

生成PDF的核心逻辑:

const puppeteer = require("puppeteer-core");
const chromium = require("@sparticuz/chromium");

async generatePdf(html = ''): Promise<Buffer> {    
    try {
        const browser = await puppeteer.launch({
            executablePath: await chromium.executablePath(),
            headless: true,
            defaultViewport: chromium.defaultViewport,
            ignoreHTTPSErrors: true,
            args: [
              ...chromium.args,
              '--no-sandbox',
              '--disable-setuid-sandbox',
              '--disable-dev-shm-usage',
              '--single-process',
              '--headless',
              '--disable-gpu',
              '--disable-features=AudioServiceOutOfProcess',
              '--no-zygote',
            ],
            pipe: true
        });

        const page = await browser.newPage();
        await page.setContent(html, { waitUntil: 'domcontentloaded' });

        const pdfBuffer = await page.pdf({
            displayHeaderFooter: true,
            format: 'a4',
            margin: { top: 24, bottom: 50, left: 24, right: 24 },
            footerTemplate: (`
              <div style="display: table-footer-group; position: fixed; font-family: 'Open Sans', Verdana, Helvetica,  sans-serif; font-size: 8px; width: 545px; text-align:right; padding: 0 20px;">
                  <div>Page <span class="pageNumber"></span>/<span class="totalPages"></span></div>
              </div>
            `)
        });
        await page.close();
        await browser.close();

        return pdfBuffer;
    } catch(err) {
        console.log(err);
        throw err;
    }
}

Docker配置

容器化Lambda的Dockerfile:

FROM public.ecr.aws/lambda/nodejs:16 as builder
WORKDIR /var/task/
COPY package.json  ./
COPY package-lock.json  ./
COPY locales ./locales
RUN npm install

FROM public.ecr.aws/lambda/nodejs:16
WORKDIR ${LAMBDA_TASK_ROOT}
ADD ./ ${LAMBDA_TASK_ROOT}
COPY --from=builder /var/task/ /
ENTRYPOINT [ "/lambda-entrypoint.sh" ]

SAM部署模板(template.yaml)

AWSTemplateFormatVersion: 2010-09-09
Description: >
  Todo

Transform: "AWS::Serverless-2016-10-31"

Parameters:
  NodeEnv:
    Type: String
    Default: "default"
  AwsRegion:
    Type: String
    Default: "default"
  AppEnv:
    Type: String
    Default: "default"
  SGid:
    Type: String
    Default: "default"
  SubnetIds:
    Type: List<String>
    Default: "default"

Globals:
  Function:
    Runtime: nodejs16.x
    MemorySize: 2024
    Timeout: 100
    Environment:
      Variables:
        NODE_ENV: !Ref NodeEnv
    VpcConfig:
      SecurityGroupIds:
        - !Ref SGid
      SubnetIds: !Ref SubnetIds

Resources:
  ChromiumLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      Description: Chromium with Node.js integration for AWS Lambda
      ContentUri: layers/chromium
      CompatibleRuntimes:
        - &nodejsRuntime nodejs16.x
      CompatibleArchitectures:
        - &chromiumArch x86_64
      RetentionPolicy: Delete
    Metadata:
      BuildMethod: *nodejsRuntime
      BuildArchitecture: *chromiumArch

  ReportFunction:
    Type: AWS::Serverless::Function
    Metadata:
    Properties:
      Architectures:
        - *chromiumArch
      Layers:
        - !Ref ChromiumLayer
      Timeout: 30
      MemorySize: 2024
      Handler: report/src/main.handler
      CodeUri: dist/apps
      Description:
      Events:
        Report:
          Type: Api
          Properties:
            Path: /report
            Method: ANY

环境版本

  • puppeteer-core 19.8.0
  • chromium 112
  • Docker desktop 4.26.1 (131620)
  • Mac OS 14.2.1(M1芯片)

故障现象

  • 约90%请求失败,抛出错误:
[Nest] 26  - 12/25/2023, 1:18:55 PM   ERROR [ExceptionsHandler] Protocol error (Page.printToPDF): Target closed
ProtocolError: Protocol error (Page.printToPDF): Target closed
    at new Callback (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:61:35)
    at CallbackRegistry.create (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:106:26)
    at Connection._rawSend (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:216:26)
    at CDPSessionImpl.send (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:419:78)
    at CDPPage.createPDFStream (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Page.js:741:88)
    at CDPPage.pdf (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Page.js:767:37)
    at FilesService.generatePdf (/var/task/files/src/files.service.js:225:42)
  • 偶尔返回损坏PDF,无法打开,终端查看为乱码。

解决方案

1. 优化页面加载等待策略

将page.setContent的waitUntil从domcontentloaded改为networkidle0,确保页面所有资源加载完成后再生成PDF:

await page.setContent(html, { waitUntil: 'networkidle0' });

如果页面有持续网络请求,可改用networkidle2(等待2秒内没有超过2个网络请求)。

2. 精简并优化Puppeteer启动参数

移除重复参数(如--headless已包含在chromium.args中),调整参数避免进程异常:

args: [
  ...chromium.args,
  '--no-sandbox',
  '--disable-setuid-sandbox',
  '--disable-dev-shm-usage',
  '--disable-gpu',
  '--disable-features=AudioServiceOutOfProcess',
  '--no-zygote',
  // 新增避免Chrome被后台挂起的参数
  '--disable-background-timer-throttling',
  '--disable-backgrounding-occluded-windows',
  '--disable-renderer-backgrounding'
],

移除--single-process,该参数在Lambda环境下易导致进程崩溃。

3. 确保Chromium层架构匹配

本地M1为arm64架构,Lambda运行环境为x86_64,需跨架构构建Chromium层:

  • 使用Docker buildx构建:
docker buildx build --platform linux/amd64 -t chromium-layer .
  • 直接使用@sparticuz/chromium官方提供的预构建层,避免自行打包架构不匹配问题。

4. 完善资源释放逻辑

在finally块中确保页面和浏览器资源被释放,避免资源泄漏:

async generatePdf(html = ''): Promise<Buffer> {    
    let browser;
    let page;
    try {
        browser = await puppeteer.launch({
            // 启动参数...
        });
        page = await browser.newPage();
        await page.setContent(html, { waitUntil: 'networkidle0' });

        const pdfBuffer = await page.pdf({
            // PDF配置...
        });
        return pdfBuffer;
    } catch(err) {
        console.error(err);
        throw err;
    } finally {
        if (page) await page.close().catch(() => {});
        if (browser) await browser.close().catch(() => {});
    }
}

5. 调整Lambda资源配置

  • 将Lambda内存提升至3008MB(更高内存对应更高CPU和带宽)
  • 超时时间从30秒调整为60秒,给PDF生成足够时间

6. 移除不必要的VPC配置

如果Lambda无需访问VPC内资源,删除VpcConfig配置,VPC内的Lambda可能存在网络延迟或资源限制,导致Chromium启动失败。

7. 验证PDF返回的Content-Type

确保API返回PDF时设置正确的响应头:

// 示例:NestJS中设置响应头
@Get('/report')
async getReport(@Res() res: Response) {
  const pdfBuffer = await this.filesService.generatePdf(html);
  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', 'inline; filename="report.pdf"');
  res.send(pdfBuffer);
}

避免响应头错误导致PDF文件传输损坏。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:54:53