1

我有一个模型,我使用 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 秒。此页面呈现意味着对“user.avatar.cropped”的大约 1200 次调用。差异似乎是由于 SSD 与 HDD (或者可能是由于 VM 的开销,不确定)。不过,OS 磁盘缓存确实起作用了,因为在服务器中第二次渲染同一页面需要 10 秒,大概是因为磁盘命中被缓存了。

如果我“手动”生成 URL,而不是使用 CarrierWave,它会在所有机器中恢复到 10 秒,所以肯定是 Carrierwave 导致了速度变慢。

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

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

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

4

1 回答 1

4

我在使用 Carrierwave 和 S3 时遇到了同样的问题。

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

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

这适用于公共 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-500 毫秒

希望这可以帮助!

于 2014-01-31T21:57:30.920 回答