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

如何在Rails中优化Wicked gem的PDF批量生成速度?

WickedPdf批量生成PDF性能优化方案

1. 复用控制器实例,减少初始化开销

每次生成PDF都新建ActionController::Base实例会重复初始化视图上下文、helper等资源,复用实例可大幅降低Ruby层面的开销:

# 提前初始化一次控制器
controller = ActionController::Base.new
pdfs = []

ticket_orders.each do |ticket_order|
  html = controller.render_to_string(
    template: "ticket_managements/_render_pdf.html.erb",
    locals: { ticket_order: ticket_order }
  )
  pdfs << WickedPdf.new.pdf_from_string(html)
end

2. 并行生成PDF,利用多核CPU

WickedPdf依赖的wkhtmltopdf是外部进程,不受Ruby GIL限制,可通过多线程/进程池并行处理任务:

# 用Ruby原生线程实现(需注意线程安全)
mutex = Mutex.new
pdfs = []
threads = ticket_orders.map do |ticket_order|
  Thread.new do
    controller = ActionController::Base.new
    html = controller.render_to_string(
      template: "ticket_managements/_render_pdf.html.erb",
      locals: { ticket_order: ticket_order }
    )
    pdf = WickedPdf.new.pdf_from_string(html)
    mutex.synchronize { pdfs << pdf }
  end
end
threads.each(&:join)

也可使用concurrent-ruby的线程池更优雅地控制并发数:

require 'concurrent'

pool = Concurrent::FixedThreadPool.new(4) # 根据CPU核心数调整,建议2-4倍核心数
pdfs = []

ticket_orders.each do |ticket_order|
  pool.post do
    controller = ActionController::Base.new
    html = controller.render_to_string(
      template: "ticket_managements/_render_pdf.html.erb",
      locals: { ticket_order: ticket_order }
    )
    pdf = WickedPdf.new.pdf_from_string(html)
    pdfs << pdf
  end
end
pool.shutdown
pool.wait_for_termination

3. 优化HTML渲染效率

  • 预加载数据库数据:检查模板中的N+1查询问题,提前用includes/joins关联加载所有需要的数据,避免循环中重复查询数据库。
  • 精简CSS/JS:移除模板中不必要的全局样式框架(如Bootstrap冗余代码)、未使用的JavaScript,减少wkhtmltopdf的解析耗时。
  • 静态化外部资源:将实时生成的图片、图标提前上传至本地或CDN,用静态URL引用,避免渲染时的IO等待。

4. 调整wkhtmltopdf核心参数

通过禁用非必要功能减少渲染负载:

WickedPdf.new.pdf_from_string(html,
  disable_javascript: true,          # 无JS渲染需求时启用
  disable_external_links: true,      # 阻止加载外部链接
  enable_local_file_access: true,    # 允许加载本地资源
  load_timeout: 5,                   # 缩短资源加载超时时间
  lowquality: true                   # 非高清需求时启用,降低文件大小和生成时间
)

5. 缓存公共内容

若多个PDF包含重复的头部、尾部或固定模块,提前预渲染这些片段并复用:

# 预渲染公共头部
header_html = controller.render_to_string(template: "ticket_managements/_pdf_header.html.erb")

ticket_orders.each do |ticket_order|
  content_html = controller.render_to_string(
    template: "ticket_managements/_pdf_content.html.erb",
    locals: { ticket_order: ticket_order }
  )
  full_html = "#{header_html}#{content_html}"
  pdfs << WickedPdf.new.pdf_from_string(full_html)
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 02:35:20