如何使用Sidekiq作为后台处理来渲染大型PDF,以避免Heroku的HTTP请求超时30秒?

时间:2019-05-23 00:10:12

标签: amazon-web-services heroku ruby-on-rails-5 sidekiq wicked-pdf

我不是专家,但是我数小时的谷歌搜索和堆栈溢出并没有给我解决问题的答案,因此我决定提出自己的问题。

我正在使用Ruby on Rails 5,正在尝试在AWS上使用Cloud9 IDE。该应用是使用Heroku部署的,该请求的HTTP请求超时时间为30秒。我需要从html.erb文件中生成一个PDF,该文件中包含很多逻辑和查询,因此通常需要100秒左右的时间才能完成,并且由于它发生在控制器中,因此被视为HTTP请求,并且需要少于30秒的时间或作为后台过程完成。如果您知道另一种解决Heroku 30秒HTTP请求超时的方法,请告诉我。

我在另一篇文章中对此进行了询问,并得到了反馈,以尝试使用带有Rails的Sidekiq之类的东西来处理大量过程,而不是通过HTTP请求进行尝试。这里的想法是将其放在后台请求中,并使其在整个100秒钟内完成其工作,然后以某种方式将PDF返回给最终用户(例如自动下载PDF)。我决定去做,让我的代码工作到可以同时运行Redis服务器(Sidekiq需要),Sidekiq服务器和常用的Rails服务器的地步,以允许我从中加载和渲染PDF。 Sidekiq工作者而不是控制器。

我的问题是'render'方法在工作人员中不可用!我试图通过使用直接从源中访问它 av = ActionView::Base.new()  接着 av.render #pdf code here

但是在我的Sidekiq控制台中出现以下错误:

“ WARN:NameError:未初始化的常量PDFWorker :: ActionView”

我的控制器中的代码:

# /app/controllers/recentgrad_controller.rb 
require 'sidekiq'
require "redis"
class RecentgradController < ApplicationController
  def report
    # things that prepare the name of the pdf, etc. go here

    PDFWorker.perform_async(pdf_name, pdf_year)
    redirect_to emphs_path
  end
end

我的工作人员中的代码:

# /app/workers/pdf_worker.rb
Sidekiq.configure_client do |config|
  # config.redis = { db: 1 }
  config.redis = { url: 'redis://172.31.6.51:6379/0' }
end

Sidekiq.configure_server do |config|
  # config.redis = { db: 1 }
  config.redis = { url: 'redis://172.31.6.51:6379/0' }
end  

class PDFWorker
  include Sidekiq::Worker
  sidekiq_options retry: false
  def perform(pdf_name, pdf_year)
    # create an instance of ActionView, so we can use the render method outside of a controller
    av = ActionView::Base.new() # THIS is where the error comes from
    av.view_paths = ActionController::Base.view_paths
    av.class_eval do
      include ActionController::UrlWriter
      include ApplicationHelper
    end

    av.render pdf: "mypdf", 
      disposition: 'attachment',
      page_height: 1300,
         encoding: 'utf8',
        page_size:   'A4',
           footer: {html: {template: 'recent_grad/footer.html.erb'}, spacing: 0 },
           margin:  {   top:    10,                     # default 10 (mm)
                        bottom: 20,
                        left:   10,
                        right:  10 },
         template: "recent_grad/report.html.erb",
           locals: {start: @start, survey: @survey, years: @years, college: @college, department: @department, program: @program, emphasis: @emphasis, questions: @questions}
  end
end


运行生成PDF的程序部分时出现错误:

WARN: NameError: uninitialized constant PDFWorker::ActionView

2 个答案:

答案 0 :(得分:0)

Sidekiq作业不是控制器操作,它没有隐式状态,例如参数,查询字符串,请求标头,Cookie等。

您可能会发现直接渲染模板更简单。我不确定您要使用什么将HTML输出转换为PDF,我在系统中使用wkhtmltopdf。这是我呈现一个简单的HTML ERB模板并将其转换为PDF的方式,以便我可以通过电子邮件将其发送给客户。

    require 'erb'

    localvar = "this is visible to the template"
    content = ERB.new(File.read("some_template.html.erb")).result(binding)

    tmpf = Tempfile.new(['sometemplate', '.html'])
    tmpf.write(content)
    tmpf.close

    result = `wkhtmltopdf #{tmpf.path} #{tmpf.path}.pdf 2>&1`
    raise result if $?.exitstatus != 0

答案 1 :(得分:0)

事实证明,使用作业而不是使用工作程序要容易得多,因为作业使您可以使用从free(): invalid pointer继承的render函数。

这里真正的赢家是@unixmonkey,他是邪恶的pdf for rails的创建者,他为我安装了一个可运行的应用程序。您可以在这里看到问题:https://github.com/mileszs/wicked_pdf/issues/835

可以在这里查看真正改变其工作方式的提交: https://github.com/unixmonkey/generate_pdf_async_example/commit/85e8dcd98fe6580a8692842fcc5316b931ce4eea

享受!