Как я могу использовать Sidekiq для рендеринга больших PDF-файлов в качестве фонового процесса, чтобы избежать 30-секундного тайм-аута Heroku для HTTP-запросов? - PullRequest
1 голос
/ 23 мая 2019

Я не эксперт, но мои часы поиска в Google и переполнения стека не дали мне ответа на мою проблему, поэтому я решил задать свой вопрос.

Я использую Ruby on Rails 5, с которой я работаю в Cloud9 IDE на AWS.Приложение разворачивается с использованием Heroku, который имеет 30-секундный таймаут для HTTP-запросов.Мне нужно сгенерировать PDF-файл из файла html.erb, в котором много логики и запросов, поэтому обычно для его завершения требуется около 100 секунд, и, поскольку это происходит в контроллере, он считается запросом HTTP иТребуется либо занять менее 30 секунд или завершить в фоновом режиме.Если вам известен другой способ обойти 30-секундный тайм-аут HTTP-запроса Heroku, ПОЖАЛУЙСТА, дайте мне знать.

Я спросил об этом в другом посте и получил отзыв, чтобы попытаться использовать что-то вроде Sidekiq с рельсами для обработки массивного процесса вместо попытки его с HTTP-запросом.Идея заключалась в том, чтобы поместить его в фоновый запрос и дать ему выполнить свою работу в течение полных 100 с лишним секунд, а затем каким-то образом вернуть PDF конечному пользователю (например, загрузить его автоматически).Я решил пойти на это и довел свой код до такой степени, что у меня был сервер Redis (требуется для Sidekiq), сервер Sidekiq и обычный сервер rails, работающие в унисон, чтобы позволить мне загружать и отображать PDF изSidekiq рабочий вместо контроллера.

Моя проблема в том, что метод render не доступен для рабочих!Я попытался получить к нему доступ непосредственно из источника, используя av = ActionView::Base.new(), а затем av.render #pdf code here

, но получил следующую ошибку в моей консоли Sidekiq:

"ПРЕДУПРЕЖДЕНИЕ: 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 голосов
/ 29 мая 2019

Оказалось, что гораздо проще использовать задание вместо работника, поскольку задания позволяют использовать функцию рендеринга, унаследованную от ApplicationController

Настоящий победитель здесь - @unixmonkey, создатель злого PDF для рельсов, и он создал для меня рабочее приложение. Вы можете увидеть вопрос здесь: https://github.com/mileszs/wicked_pdf/issues/835

А коммит, который действительно изменил способ его работы, можно посмотреть здесь: https://github.com/unixmonkey/generate_pdf_async_example/commit/85e8dcd98fe6580a8692842fcc5316b931ce4eea

Наслаждайтесь!

0 голосов
/ 23 мая 2019

Задание 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
...