【发布时间】:2019-10-08 07:40:41
【问题描述】:
我继承了一个使用 Heroku 部署的 Rails 应用程序(我认为)。我在 AWS 的 Cloud9 IDE 上对其进行了编辑,现在只在开发模式下进行所有操作。该应用程序的目的是处理大量调查数据并将其输出到 PDF 报告中。这适用于具有 10 行数据的小型报表,但是当我加载一个查询 5000 多行数据上传的报表以创建一个转换为 PDF 的 HTML 页面时,大约需要 105 秒,比 Heroku 的要长得多为 HTTP 请求分配 30 秒。
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 秒!”是查询的形式吗?因为,如果是这样,我会在我的 report.html.erb 文件中一遍又一遍地查询 MySql 数据库。
此外,它过去可以正常工作并且可以处理小型报告,但现在我在实际页面上的请求完成之前收到错误“504 Gateway Timeout”,但我的 puma 控制台继续像查询数据库一样查询数据库疯子。我认为这是 Heroku 问题,因为 504 错误恰好每 35 秒发生一次(5 秒处理其他部分,30 秒尝试完成模板中的循环,以便正确渲染)。
如果您需要更多信息或代码,请询问!提前致谢
编辑: 下面的两个 cmets 都建议了可能的重复项,但是他们都没有真正的代码的真正答案,他们只是参考我在这里引用的文档。我正在寻找一个代码示例(或者至少是一种让我踏上大门的方法),而不仅仅是指向文档的链接。谢谢!
编辑2:
我尝试了@Sergio 所说的并安装了 SideKiq。我想我真的很接近,但仍然与工人有一些问题。工作人员无权访问 Rails 中的渲染方法所需的 ActionView::Base,因此它不起作用。我可以访问 worker 方法,这意味着我的 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 所说的去做,并尝试直接从 html.erb 文件制作 PDF 并将其保存到文件中。这是我的代码:
# /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 种变体。
【问题讨论】:
-
参见上面的编辑,我正在寻找如何做到这一点,而不仅仅是在文档中的何处找到它们。似乎其他两个帖子只是将提问者指向我在问题中引用的文档。
-
“我的模板在一个有限循环中查询数据库,当它用完要查询的调查问题时停止。” - 我在这里闻到一箱 N+1
-
当我从 ApplicationJob 继承一份工作而不是工作人员时,结果证明它起作用了,尽管它们本质上是一样的。这是我的继承:
class CreatePdfJob < ApplicationJob。这里真正的赢家是@unixmonkey,他为rails 维护wicked_pdf,他为我设置了一个工作应用程序。您可以在此处查看问题:github.com/mileszs/wicked_pdf/issues/835 可以在此处查看真正改变其工作方式的提交:github.com/unixmonkey/generate_pdf_async_example/commit/... 享受!
标签: ruby-on-rails heroku cloud9-ide puma