2017-06-09 58 views
3

我试图用Rails生成PDF文件,但是当我这样做时我注意到我的系统CPU开始最大化。最初,它将从大约2.5%上升,然后在稳定的时间段内增加到大约65%-80%,然后在我的页面的iframe中显示PDF之前,最终达到最大值。这里有一些消息监视我的系统上的内存使用情况,当我得到:使用Wicked_PDF在Rails中生成PDF时的高CPU使用率gem

Warning or critical alerts (lasts 9 entries) 
          2017-06-09 14:58:07 (0:00:04) - CRITICAL on CPU_SYSTEM (100.0) 
          2017-06-09 14:58:04 (0:00:13) - CRITICAL on CPU_USER (Min:72.8 Mean:83.3 Max:93.7) 
          2017-06-09 14:47:39 (0:00:06) - CRITICAL on CPU_USER (93.0) 
          2017-06-09 14:47:29 (0:00:04) - WARNING on CPU_SYSTEM (74.7) 
          2017-06-09 14:36:48 (0:00:04) - CRITICAL on CPU_SYSTEM (100.0) 
          2017-06-09 14:36:45 (0:00:10) - CRITICAL on CPU_IOWAIT (Min:78.6 Mean:85.7 Max:97.4) 
          2017-06-09 14:18:06 (0:00:04) - CRITICAL on CPU_SYSTEM (94.3) 
          2017-06-09 14:18:06 (0:00:07) - CRITICAL on CPU_USER (91.0) 
2017-06-09 15:01:14  2017-06-09 14:17:44 (0:00:04) - WARNING on CPU_SYSTEM (73.8) 

我已经安装了我的PDF生成的宝石wicked_pdf (1.0.6)wkhtmltopdf-binary-edge (0.12.4.0)。并且与每个代码的过程如下:

控制器/关切/ pdf_player_reports.rb

def director_report_pdf 
    @players = Player.where(id: params["player_ids"] 

    respond_to do |format| 
    format.html 
    format.pdf do 
    render pdf: "#{params['pdf_title']}", 
     template: 'players/director_summary_report.pdf.erb', 
     layout: 'print', 
     show_as_html: params.key?('debug'), 
     window_status: 'Loading...', 
     disable_internal_links: true, 
     disable_external_links: true, 
     dpi: 75, 
     disable_javascript: true, 
     :margin => {:top => 7, :bottom => 7, :left => 6, :right => 0}, 
     encoding: 'utf8' 
    end 
end 

播放器/ director_summary_report.pdf.erb

<div class="document" style="margin-top: -63px;"> 
    <% @players.each do |player| %> 
    <% reports = player.reports.order(created_at: :desc) %> 
    <% if player.is_college_player? %> 
     <%= render partial: 'college_director_report.html.erb', player: player %> 
    <% else %> 
     <%= render partial: 'pro_director_report.html.erb', player: player %> 
    <% end %> 
    <%= "<div class='page-break'></div>".html_safe %> 
    <% end %> 
</div> 

college_director_report。 html.erb

<%= wicked_pdf_stylesheet_link_tag "application", media: "all" %> 
<%= wicked_pdf_javascript_include_tag "application" %> 
<% provide(:title, "#{player.football_name}") %> 
<% self.formats = [:html, :pdf, :css, :coffee, :scss] %> 

<style> 
    thead { display: table-row-group; page-break-inside: avoid } 
    tfoot { display: table-row-group; } 
    /*thead:before, thead:after { display: none; }*/ 
    table { page-break-inside: avoid; } 
    tr { page-break-inside: avoid; } 
    .page-break { 
     display:block; clear:both; page-break-after:always; 
    } 
    .keep-together { page-break-before: always !important; } 
    .table-striped>tbody>tr:nth-child(odd)>td, 
    tr.found{ 
     background-color:#e2e0e0 !important; 
    } 
</style> 

<div class="row"> 
    <div class="col-xs-6"> 
     <span>DIRECTOR SUMMARY</span> 
    </div> 
    <div class="col-xs-6 text-right"> 
     <%= "#{player.full_name}/#{player.school.short_name}".upcase %> 
     <h1><%= "#{player.full_name(true)} (#{player.school.code})".upcase %></h1> 
    </div> 
</div> 

<div class="row"> 
    <div class="col-xs-12"> 
    <%= render 'directors_report_player_header', player: player %> 
    <%= render 'directors_report_workouts', player: player %> 
    <%= render 'directors_report_grades', player: player %> 
    <%= render 'legacy_directors_report_contacts', player: player %> 
    </div> 
</div> 

directors_report_player_header.html.erb

<table class="table table-condensed table-bordered"> 
    <thead> 
     <tr> 
      <th>Name</th> 
      <th>School</th> 
      <th>#</th> 
      <th>Position</th> 
     </tr> 
    </thead> 
    <tbody> 
     <tr> 
      <td><%= player.full_name(true) %></td> 
      <td><%= player.school.short_name %></td> 
      <td><%= player.jersey %></td> 
      <td><%= player.position.abbreviation %></td> 
     </tr> 
    </tbody> 
</table> 

UPDATE

我用下面和CPU%是什么最终杏,如下图所示跑了一个例子PDF生成器...

enter image description here

<table class="table table-condensed"> 
    <thead> 
     <th>Number</th> 
    </thead> 
    <tbody> 
     <% (1..60000).each do |number| %> 
     <tr> 
      <td><%= number %></td> 
     </tr> 
     <% end %> 
    </tbody> 
    </table> 
+0

你在哪里举办? – jvillian

+0

@jvillian,我在内部Ubuntu服务器上托管14.04 LTS服务器,带有1CPU,16GB,但是我的本地机器上运行的是最新的Ubuntu桌面版本8GB,结果相同 – daveomcd

+0

在我的情况中,我很好当地的,但在Heroku有一个记忆失控。罪魁祸首是wkhtmltopdf-binary。我切换到wkhtmltopdf-heroku,这一切都理顺了。也许看看它? – jvillian

回答

2

把它放在控制器中似乎不太合适,因为部署这个请求的那一分钟将需要很长时间来生成和阻止其他页面的其他传入请求。

你应该把它分成两个问题。一个生成HTML的作业,可能是该控制器,然后是将该HTML转换为PDF格式的后台任务。

在您的控制器中,使用DelayedJob或类似条件触发作业,然后呈现轮询已完成作业的页面。

然后在您的后台作业中,您只处理将HTML呈现为PDF的任务,而不是处于Web请求中。沿着这些路线的东西:

class RendersReportPdf 
    def self.call player_ids 
    html = ReportsController.render :director_report_pdf, assigns: { players: Player.where(id: player_ids } 
    pdf = WickedPdf.new.pdf_from_string html  
    temp = Tempfile.new("#{Time.now.to_i}.pdf") 
    temp.write(pdf) 
    temp.close 
    temp.path 
    # Probably upload this to S3 or similar at this point 
    # Notify the user that it's now available somehow 
    end 
end 

如果你这样做,那么你就可以排除这个问题是从你的控制器动作中运行WickedPDF,而且,你要确保你的网站将会熬夜,如果你有长时间运行的请求。

+0

谢谢!对不起,迟迟不能回复你。所以你说我应该创建两个不同的关注文件:(1)用于生成HTML,(2)用于生成HTML到PDF。然后执行一个将按顺序执行两个的活动作业? – daveomcd

+0

轮到我道歉 - 短假!是的,我认为将所有这一切结合在一起会在您部署它时立即引起麻烦。最好将这些分开,然后再看看是否导致事物锁定。 – stef

0

因此,我想为未来的访问者发布我的解决方案,但它基于@ stef的解决方案 - 非常感谢stef!

controllers/concerns/players_controller。RB

def generate_report_pdf 
    players = print_settings(params) 
    pdf_title = "#{params['pdf_title']} - #{Time.now.strftime("%c")}" 
    GeneratePdfJob.perform_later(players.pluck(:id), pdf_title, current_user.code, params["format"]) 
    end 

应用程序/工作/ generate_pdf_job.rb

def perform(*args) 

    player_ids = args[0] 
    pdf_title = args[1] 
    user_code = args[2] 
    report_type = args[3] 

    generate_pdf_document(player_ids, pdf_title, user_code, report_type) 

    end 

    def generate_pdf_document(ids, pdf_title, user_code, report_type) 

    # select the proper template by the report type specified 
    case report_type 
     when "Labels" 
      html = ApplicationController.new.render_to_string(
      template: 'players/board_labels.pdf.erb', 
       locals: { player_ids: ids }, 
       margin: { top: 6, bottom: 0, left: 32, right: 32 } 
      ) 
     when "Reports" 
      # ... 
    end 
    end 

    def save_to_pdf(html, pdf_title, user_code) 

    pdf = WickedPdf.new.pdf_from_string(
          html, 
          pdf: "#{pdf_title}", 
         layout: 'print', 
     disable_internal_links: true, 
     disable_external_links: true, 
      disable_javascript: true, 
        encoding: 'utf-8' 
    ) 

    pdf_name = "#{pdf_title}.pdf" 
    pdf_dir = Rails.root.join('public','uploads','reports',"#{user_code}") 
    pdf_path = Rails.root.join(pdf_dir,pdf_name) 

    # create the folder if it doesn't exist 
    FileUtils.mkdir_p(pdf_dir) unless File.directory?(pdf_dir) 

    # create a new file 
    File.open(pdf_path,'wb') do |file| 
     file.binmode 
     file << pdf.force_encoding("UTF-8") 
    end 

    end 

用这种方式,然后我用一个AJAX调用继续检查用户指定为新文件的目录,我更新了部分列出目录中的文件。我唯一不喜欢的是现在我必须有一个用户文件的表格列表。我宁愿只是将文件交付给客户端的浏览器下载,而不是 - 但还没有想出如何让它起作用。