0

まず、私の下手な英語をお詫びしたいと思います。

gem PaperClip 3.3.1 を使用してファイルのサーバー フォルダーにアップロードするコードがあります。50枚の写真を含むフォルダーをアップロードしようとするまで、どの少数の写真(最大30枚)でもすべてが見栄えがします。次に、次のエラーメッセージが表示されます。

Errno::EMFILE (Too many open files - identify -format %m '/tmp/4209520130906-10816-1kk0w0o.jpg[0]'):

私のシステムで ulimit -a をチェックすると、私の環境では同時に 1200 個のファイルを開くことができます。それで、私はコマンドを実行しました:

watch -n1 'lsof -a -p <rails server pid> | wc -l'

そして、繰り返しごとに、開かれたファイルの数が増加していました

110 - starting count
160 - after first iteration (number from folder)
240 - second iteration
320
376
457
...
1118 - of about 32 iteration
crash ~ :)

問題は、データベースにimage_pathを追加し、特別なフォーマットが必要な場合はサイズを変更するprocによって引き起こされることがわかりました(html入力解析中)

そのため、デバッガーで次のコマンドを実行して、ルビーが保持するファイルを確認しました。

ObjectSpace.each_object(File){ |f| puts f.path }

「RackMultipart」を含む名前でアップロードされた 50 個のファイルがあり、反復後に tmp フォルダー内の多数のファイルが奇妙な名前の新しいファイル (おそらくペーパークリップ一時ファイル) によってクレジットされていることがわかりました。私はpeperclipまたはいくつかのアプリコードがファイルを閉じないと思ったので、procの最後に次のコードを追加しました:

        now = Time.now.to_formatted_s( :number )[0..7]
        ObjectSpace.each_object(File).map do |f|
          if f.path.include?( "/tmp/" ) && !f.closed?
            unless f.path.include? "RackMultipart#{now}"
              f.close
              f = nil
            end
          end
        end

その後、実際には何も起こりませんでした。デバッガーでコマンドを実行しました

ObjectSpace.each_object(File){ |f| puts ( f.closed? '+' : '-' ) }

開いているファイルの数 (-) は正しかった (~160) が、コマンドはまだ閉じられたファイルをリストしていました。私がそれらを閉じた後、それらは消えるべきではありませんか?

切り捨てられたメソッドは次のようになります。

def function(g, files)

    ...

    asset = proc do |img|
      ...
      file = files.find{|f| f.original_filename == src}
      if file

        geo = Paperclip::Geometry.from_file file.path

        if(geo.width <= 20 && geo.height <= 20)
          ...
        else
          asset = figure.assets.build
          asset.file = file ## HERE RUNS
          ...
        end

        now = Time.now.to_formatted_s( :number )[0..7]
        ObjectSpace.each_object(File).map do |f|
          if f.path.include?( "/tmp/" ) && !f.closed?
            unless f.path.include? "RackMultipart#{now}"
              f.close
              f = nil
            end
          end
        end
      else
        ...
      end
    end

    ...

      while img = g.find_first(".//img")
        asset.call(img) # !HERE HAPPENS
        img.remove!
      end

 end # end function

資産モデルの定義:

class Asset < ActiveRecord::Base
  ...

  has_attached_file :file, url: "/system/:class/:attachment/:id/:style_:filename",
                    styles: lambda { |attachment| attachment.instance.switch_styles },
                    :convert_options => {medium: lambda{|asset| asset.is_table? ? "-units PixelsPerInch -resample 120 -strip" : "-strip"}, 
                                            all: '-strip'}




  validates_attachment :file, content_type: { content_type: ["image/jpg","image/png","image/gif","image/jpeg","image/tiff"] },
                       size: { in: 0..10.megabytes }

  def is_table?
    ...
  end

  def switch_styles
    self.file.content_type == "image/tiff" ?
    { backup: "100%", original: ["100%", :png], medium: [self.is_table? ? "" : "800x600>", :png], small: ["300x300>", :png], formula: ['50%', :png] } :
    { medium: self.is_table? ? "" : "800x600>", small: "300x300>", formula: '50%' }
  end

end

私が書いたことを理解していただければ幸いです;)

4 よろしくお願いします。

4

1 に答える 1

1

Ok。それはこのコードに問題はなく、すべて正常に動作します。しかし、誰かが親の親クラスで GarbageColector を無効にしたことがあります。おそらく解析プロセスを高速化するためです。そのため、ファイル数が ulimit の 70% を超えたときに GC をオンにし、不要な一時ファイルを消去する場合は再度オフにします。


編集:GCを実行してから再度無効にする機能

  def gc_check
    if ObjectSpace.each_object(File).to_a.size > 850  
      GC.start if GC.enable
    else
      GC.disable
    end
  end

これがお役に立てば幸いです。

于 2013-09-06T12:48:59.063 に答える