我有一个模型,我正在使用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
希望这有帮助!