2017-06-09 94 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調用繼續檢查用戶指定爲新文件的目錄,我更新了部分列出目錄中的文件。我唯一不喜歡的是現在我必須有一個用戶文件的表格列表。我寧願只是將文件交付給客戶端的瀏覽器下載,而不是 - 但還沒有想出如何讓它起作用。