Я унаследовал приложение rails, которое развертывается с помощью Heroku (я думаю). Я редактирую его в AWS Cloud9 IDE и пока просто делаю все в режиме разработки. Цель приложения — обрабатывать большие объемы данных опроса и отображать их в отчете в формате PDF. Это работает для небольших отчетов с примерно 10 строками данных, но когда я загружаю отчет, который запрашивает загрузку данных из 5000+ строк для создания HTML-страницы, которая преобразуется в PDF, это занимает около 105 секунд, что намного дольше, чем у Heroku. 30 секунд отводится для HTTP-запросов.
Heroku говорит об этом на своем веб-сайте, что вселило в меня некоторую надежду:
«Heroku поддерживает функции HTTP 1.1, такие как длинный опрос и потоковые ответы. Приложение имеет начальное 30-секундное окно, чтобы ответить клиенту одним байтом. Однако каждый байт, переданный после этого (будь то получен от клиента или отправлен вашим приложение) сбрасывает скользящее 55-секундное окно. Если в течение 55-секундного окна данные не отправляются, соединение будет разорвано». (Источник: https://devcenter.heroku.com/articles/request-timeout#long-polling-and-streaming-responses)
Для меня это звучит превосходно - я могу просто отправлять запрос клиенту каждую секунду или около того в цикле, пока мы не закончим создание большого отчета в формате PDF. Однако я не знаю, как отправить или получить байт или около того, чтобы «сбросить скользящее 55-секундное окно», о котором они говорят.
Вот часть моего контроллера, которая отправляет запрос.
return render pdf: pdf_name + " " + pdf_year.to_s,
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}
Я делаю другие запросы, чтобы добраться до этого момента, но я считаю, что часть, вызывающая проблему, находится здесь, где отображается шаблон. Мой шаблон запрашивает базу данных в конечном цикле, который останавливается, когда заканчиваются вопросы опроса для запроса.
Мой вопрос таков: как я могу «отправить или получить байт клиенту», чтобы сообщить Heroku: «Я все еще пытаюсь создать этот массивный PDF-файл, поэтому, пожалуйста, сбросьте таймер и дайте мне мои 55 секунд!» Это в форме запроса? Потому что, если это так, я снова и снова запрашиваю базу данных MySql в своем файле report.html.erb.
Кроме того, раньше он работал без проблем и работал с небольшими отчетами, но теперь я получаю сообщение об ошибке «504 Gateway Timeout» до завершения запроса на фактической странице, но моя консоль puma продолжает запрашивать базу данных, как сумасшедший. Я предполагаю, что это проблема Heroku, потому что ошибка 504 происходит ровно каждые 35 секунд (5 секунд для обработки других частей и 30 секунд, чтобы попытаться завершить цикл в шаблоне, чтобы он мог правильно отображаться).
Если вам нужна дополнительная информация или код, пожалуйста, спросите! заранее спасибо
РЕДАКТИРОВАТЬ: Оба приведенных ниже комментария предполагают возможные дубликаты, но ни один из них не имеет реального ответа с реальным кодом, они просто ссылаются на документы, которые я здесь цитирую. Я ищу пример кода (или, по крайней мере, способ войти в дверь), а не просто ссылку на документы. Спасибо!
РЕДАКТИРОВАТЬ 2:
Я попробовал то, что сказал @Sergio, и установил SideKiq. Я думаю, что я действительно близок, но все еще имею некоторые проблемы с работником. У работника нет доступа к ActionView::Base, который требуется для метода рендеринга в рельсах, поэтому он не работает. Я могу получить доступ к рабочему методу, что означает, что мои серверы sidekiq и redis работают правильно, но он попадает в строку ActionView с этой ошибкой:
ПРЕДУПРЕЖДЕНИЕ: NameError: неинициализированная константа HardWorker::ActionView
Вот рабочий код:
require 'sidekiq'
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 HardWorker
include Sidekiq::Worker
def perform(pdf_name, pdf_year)
av = ActionView::Base.new()
av.view_paths = ActionController::Base.view_paths
av.class_eval do
include Rails.application.routes.url_helpers
include ApplicationHelper
end
puts "inside hardworker"
puts pdf_name, pdf_year
av.render pdf: pdf_name + " " + pdf_year.to_s,
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
Какие-либо предложения?
РЕДАКТИРОВАТЬ 3: я сделал то, что сказал @Sergio, и попытался создать PDF-файл напрямую из файла html.erb и сохранить его в файл. Вот мой код:
# /app/controllers/recentgrad_controller.rb
pdf = WickedPdf.new.pdf_from_html_file('home/ec2-user/environment/gradSurvey/gradSurvey/app/views/recent_grad/report.html.erb')
save_path = Rails.root.join('pdfs', pdf_name + pdf_year.to_s + '.pdf')
File.open(save_path, 'wb') do |file|
file << pdf
end
И вывод ошибки:
RuntimeError (Failed to execute:
["/usr/local/rvm/gems/ruby-2.4.1@gradSurvey/bin/wkhtmltopdf", "file:///home/ec2-user/environment/gradSurvey/gradSurvey/app/views/recent_grad/report.html.erb", "/tmp/wicked_pdf_generated_file20190523-15416-hvb3zg.pdf"]
Error: PDF could not be generated!
Command Error: Loading pages (1/6)
Error: Failed loading page file:///home/ec2-user/environment/gradSurvey/gradSurvey/app/views/recent_grad/report.html.erb (sometimes it will work just to ignore this error with --load-error-handling ignore)
Exit with code 1 due to network error: ContentNotFoundError
):
Я понятия не имею, что это значит, когда он говорит: «иногда будет работать просто игнорировать эту ошибку с --load-error-handling ignore». Файл определенно существует, и я пробовал, может быть, 5 вариантов пути к файлу.
class CreatePdfJob < ApplicationJob
. Настоящим победителем здесь стал @unixmonkey, который поддерживает wicked_pdf для рельсов и настроил для меня работающее приложение. Посмотреть вопрос можно здесь: github.com/mileszs/wicked_pdf/issues/835 А коммит, который действительно изменил его работу, можно посмотреть здесь: github.com/unixmonkey/generate_pdf_async_example/commit/… Наслаждайтесь! - person Parzival   schedule 29.05.2019