Carrierwave:由于每次编辑都会命中HDD,因此生成数千张图像的URL时出现性能问题



我有一个模型,我正在使用Carrierwave让用户上传他们的头像。我有一个版本的头像,裁剪成一个叫做"裁剪"的正方形。

class User
  mount_uploader :avatar, AvatarUploader
end
class AvatarUploader < ImageUploader
  version :cropped do
    process :crop
  end
end

在我的一个页面中,我列出了几千个用户,每个用户都有自己的头像,为这些图像生成URL的调用需要很长时间。

问题似乎在于,仅通过访问挂载了上传器的模型属性,Carrierwave 似乎正在点击磁盘以读取文件、检查它是否存在或其他内容。换句话说,调用:

user.avatar
user.avatar.cropped
user.avatar.present?
user.avatar.anything_really

所有这些都击中了硬盘。

在我发现的最坏的情况下,对于我们拥有大量数据的客户端之一,页面在我的开发机器上呈现 10 秒,在服务器上呈现 30 秒。此页面渲染意味着大约 1200 次对"user.avatar.cropped"的调用。差异似乎是由于SSD与HDD(或者可能是由于VM的开销,不确定)。不过,操作系统磁盘缓存确实会启动,因为在服务器中第二次呈现同一页面需要 10 秒,大概是因为缓存了磁盘命中。

如果我"手动"生成URL,而不是使用CarrierWave,则在所有机器中都会恢复到10秒,因此绝对是Carrierwave导致了速度变慢。

似乎不需要点击HDD来生成路径。有没有办法避免这种情况,或者解决这个问题?

(注意:我知道 10 秒对于一个页面来说无论如何都是残酷的,我们正在做一些事情来解决这个问题,但在整个站点范围内,我们有很多页面显示大量头像,并且由于这些 HDD 点击,我们速度变慢了,所以我们想改进它们,而不必手动生成 URL,因为 Carrierwave 提供的抽象很棒)

更新(由于下面的评论):我们不使用eager_load。Cache_classes在开发和生产中关闭。(同样,dev 很快,prod 很慢,但我认为这与cache_classes无关)

我在Carrierwave和S3上遇到了同样的问题。

这是我的场景,我有一个 API 负责将头像上传到 S3,默认情况下,用户有一个头像存储在我的 API 的资产文件夹中。如果 10 个用户有一个默认头像,他们都使用相同的图像,以便将一堆默认头像上传到 S3。

我创建了一个自定义方法,基本上检查头像是否已上传。如果已上传头像,我们将根据大小手动构建头像的 S3 路径。否则,我们只返回默认的头像路径,该路径存储在本地的 assets 文件夹中。

这适用于公共 amazon s3 存储桶,我不确定私有存储桶会发生什么或如何生成过期参数,但由于您使用的是文件存储系统,因此应该不是问题。

module Illustrable
  extend ActiveSupport::Concern
  included do
    mount_uploader :avatar, AvatarUploader
  end
  def avatar_img(size)
    if self.avatar?
      "http://s3.amazonaws.com/#{ ENV['S3_BUCKET_NAME'] }/uploads/user/avatar/#{ self.id.to_s }/#{ size }_#{ self.avatar_identifier }"
    else
      ENV['DOMAIN_NAME'] + ActionController::Base.helpers.asset_path(["#{ size }_default_avatar.png"].compact.join('_'))
    end
  end
end

此指令是生成文件名并且不点击 S3 以返回 img 路径的关键。

self.avatar_identifier

我之前的时间响应为 2 秒,现在我在 300-500ms

希望这有帮助!

最新更新