default_storage.exists非常慢,经常超时

时间:2014-07-10 20:33:58

标签: python django amazon-s3 django-storage

我们刚刚将Django项目迁移到Heroku,并将所有媒体和静态文件放在Amazon S3上(使用django-storages和s3-boto)。

尽管我听说过有关亚马逊S3的速度非常快,并且由于性能下降而导致的结果很少,但我们的图像加载速度已经减慢到绝对爬行并经常超时。超时代码的一个示例是我们的某个模型上的此属性,它试图选择合适的图像,最终回退到返回None

@property
def photo(self):
    """Transparently serve the best available image for templates"""
    if self.model_shot.storage.exists(self.model_shot.name):
        return self.model_shot
    elif self.image.storage.exists(self.image.name):
        return self.image
    else:
        return None

当我在导致问题的模型上进行测试时,我尝试了这个:

$ heroku run python manage.py shell
...
>>> design = Design.objects.get(pk=10210)
>>> design.photo

此命令导致shell在最终返回ImageFieldFile对象之前挂起几秒钟。随后对它的调用立即返回,这让我相信结果是缓存的。

我的问题是,处理这个问题的最佳方法是什么?我听说过很多关于在这种情况下使用CloudFront的事情,但这绝对不是因为流量很大(我们网站上基本上没有任何流量)。其他一些缓存框架?还有其他什么呢?

大多数图像至少是1000x1000像素。

3 个答案:

答案 0 :(得分:1)

我在生产网站上遇到类似的问题,因为它扩大了规模。我建议使用的是一个存储后端,它可以维护自己的所有关于S3文件的元信息的副本。最好的此类项目可能是MimicDB,但您也可以查看我使用modified django-storages完成的工作。这样,.exists().url等元数据查询就会立即从本地缓存中回答。

此外,请确保您通常只获取image对象的URL或其他元数据,而不使用任何会导致服务器不必要地获取实际图像数据的代码。设置此类内容时我喜欢做的是修改S3包装器(例如boto),以便记录每个原始S3 REST请求,然后测试该站点并确保只是在网站上查看网页并不会; t导致来自Web服务器的任何S3请求。

答案 1 :(得分:1)

切换到CloudFront完全解决了这个问题,并且相对容易(没有代码更改只是更多地使用亚马逊控制台),所以我决定回答我自己的问题。

tl; dr不要直接从S3提供文件;设置CloudFront。


通过CloudFront提供S3 Bucket

步骤0:如果您还没有,请确保您的存储桶名称符合命名存储区的"best practices"。它们并不一定能在所有应有的地方显而易见,但糟糕的存储桶名称可能完全破坏其与其他Amazon Web Services的互操作性。最好的办法是将你的桶命名为小写,不要太长(<= 60个字符左右)。

第1步:为了让CloudFront从您的存储桶中提供文件,您需要将其设置为为静态网站提供服务。您可以从存储桶的Permissions选项卡在Amazon AWS控制台上执行此操作。亚马逊有几个地方有关于此的说明/文件; IMO最清楚的是these 重要 :确保将默认根对象设置为index.html - 该文件甚至不必存在,但该设置确实存在

步骤1.5 [可选]:确保您的存储分区的权限正确无误。即使我从S3提供文件没有问题,更改为CloudFront为他们提供服务会将所有内容转换为 403:禁止访问错误。如果有疑问,并且您的文件不敏感,您可以在AWS控制台中右键单击存储桶的文件夹,然后单击Make Public 警告 :这可能是一个非常耗时的过程,出于某些愚蠢的原因(即使它是服务器端),您的浏览器会话必须保持不变打开。首先执行此操作,不要关闭会话。对于我们的水桶,这需要大约16个小时。 :/

第2步:转到AWS控制台中的Amazon CloudFront部分,然后点击Create Distribution按钮。使其成为Web分发(默认)并使用您生成的域,方法是将上一步中的静态Web分配设置为原点。同样,IMO,these是AWS文档中最清晰,最直接的说明。你可以在这里保留几乎所有的默认值。一旦创建,只需等待它在控制台上列为“已部署”。

第3步:将您的应用配置为从CloudFront而非S3进行投放。这是最简单的部分,因为网址从https://bucketname.s3.amazonaws.com/path透明地移动到https://somerandomstring.cloudfront.net/path(奖励:您可以将后者设置为CNAME记录,指向media.yourdomain.tld之类的内容;我们没有这样做,所以我不会在这里讨论)。由于我将Django与django-storagess3-boto结合使用,最终只是setting up that Cloudfront domain in settings.py的一个简单问题:

AWS_S3_CUSTOM_DOMAIN = 'd2ynhpzeiwwiom.cloudfront.net'

就是这样!随着这些变化,我们所有的速度问题都消失了,我们富媒体页面(每页6-20万像素的图像)突然加载速度比以往任何时候都快!

答案 2 :(得分:0)

设置 AWS_PRELOAD_METADATA = False (默认值为False),以避免将存储桶中的所有文件元数据加载到内存中。这不仅会增加请求的等待时间,还会减少内存使用量。 在我的情况下,内存使用量从大约1.5 GB减少到5-6MB,时间从60s减少到4-5s。

原始答案: Why does default_storate.exists() with django-storages with S3Boto backend cause a memory error with a large S3 bucket?