生产环境中Puppeteer生成PDF超时问题求助
问题背景
以下C#代码使用PuppeteerSharp生成PDF,在测试和开发服务器运行正常,但生产服务器触发超时异常。
生成PDF的核心方法
public async Task<IActionResult> SavePDF(string content) { try { var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, }); var page = await browser.NewPageAsync(); // Set a timeout (60 sec) while loading the content await page.SetContentAsync(content, new NavigationOptions { Timeout = 60000 }); var fileName = $"C:/CustomerData/{Guid.NewGuid()}_{CompanyNumber}.pdf"; await page.PdfAsync(fileName); // Dispose of the browser properly await browser.CloseAsync(); return Json(fileName); } catch (Exception ex) { ASLog(ex); return Problem($"Error generating PDF: {ex.Message}"); } }
Program.cs初始化代码
// Download Chromium once when the application starts await new BrowserFetcher().DownloadAsync();
生产环境错误栈
2025-03-27T00:44:03.4471493+00:00 80001bae-0000-f300-b63f-84710c7967bb [ERR] An unhandled exception has occurred while executing the request. (48a46595)
System.TimeoutException: Timeout of 180000 ms exceeded
at PuppeteerSharp.Helpers.TaskHelper.WithTimeout[T](Task1 task, TimeSpan timeout, Func2 exceptionFactory) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Helpers/TaskHelper.cs:line 185
at PuppeteerSharp.Cdp.CdpCDPSession.SendAsync(String method, Object args, Boolean waitForCallback, CommandOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Cdp/CdpCDPSession.cs:line 113
at PuppeteerSharp.CDPSession.SendAsync[T](String method, Object args, CommandOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/CDPSession.cs:line 48
at PuppeteerSharp.Cdp.CdpPage.PdfInternalAsync(String file, PdfOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Cdp/CdpPage.cs:line 864
at PuppeteerSharp.Page.PdfStreamAsync(PdfOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Page.cs:line 389
at ASWeb.Controllers.MainMenu.SalesInvoicing.SalesInvoicingController.SendEmailPDF(InvoicePDFViewModel model) in C:\Code\AS-Web\ASWeb.App\Controllers\MainMenu\SalesInvoicing\SalesInvoicingController.cs:line 2401
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at ASWeb.Middlewares.TrialVersionMiddleware.Invoke(HttpContext httpContext, ISharedService shared) in C:\Code\AS-Web\ASWeb.App\Middlewares\TrialVersionMiddleware.cs:line 39
at ASWeb.Middlewares.XeroDisconnectedMiddleware.Invoke(HttpContext httpContext, ISettingsService settings) in C:\Code\AS-Web\ASWeb.App\Middlewares\XeroDisconnectedMiddleware.cs:line 22
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Program.<>c__DisplayClass0_0.<$>b__9>d.MoveNext() in C:\Code\AS-Web\ASWeb.App\Program.cs:line 210
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)
可能的原因分析
- 生产环境资源不足:生产服务器CPU、内存资源紧张,Chromium渲染PDF需要较多系统资源,导致
PdfAsync操作超时。建议检查生产服务器的资源使用率,尤其是PDF生成高峰期的负载情况。 - Headless模式兼容性问题:默认的无头模式在无GUI的生产服务器上可能存在兼容性问题。可以尝试切换到新版无头模式,或添加沙箱禁用参数:
var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = HeadlessMode.New, Args = new[] { "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage" } }); - Chromium完整性或版本问题:生产环境下载的Chromium可能因网络问题不完整,或版本与PuppeteerSharp不兼容。可手动指定Chromium路径,或重新下载确保文件完整。
- 页面资源未完全加载:虽然设置了
SetContentAsync的60秒超时,但如果内容包含大量远程资源(图片、样式),生产环境网络访问较慢会导致页面未完全渲染,进而引发PdfAsync超时。可调整超时时间并设置等待网络空闲:await page.SetContentAsync(content, new NavigationOptions { Timeout = 120000, WaitUntil = WaitUntilNavigation.Networkidle0 }); - 并发请求阻塞:生产环境大量并发PDF生成请求,每个请求启动新Chromium实例会耗尽服务器资源,导致操作排队超时。建议使用浏览器池复用实例,减少资源消耗。
- 文件系统权限问题:生成PDF到
C:/CustomerData/目录时,生产环境应用程序池可能无写入权限,导致PdfAsync操作卡住超时。需检查该目录的读写权限,确保应用身份有足够权限。
内容的提问来源于stack exchange,提问作者Pedro

