Lambda@Edge 仅为部分图像提供 502

Lambda@Edge Gives 502 only for SOME images

我们所做的是请求像 "media/catalog/product/3/0/30123/768x/lorem.jpg" 这样的图像,然后我们使用位于 "media/catalog/product/3/0/30123.jpg" 的原始图像,将其大小调整为 768px 和 webp(如果浏览器支持的话)然后 return 新图像(如果尚未缓存)。

如果您请求:wysiwyg/lorem.jpg 它将尝试创建最大 1920 像素的 webp(无放大)。

对于 <= 1420 像素宽的图像,这似乎完美无缺。然而,除此之外我们只得到 HTTP 502: The Lambda function returned invalid json: The json output is not parsable.

SO 上有一个与 GZIP 相关的类似问题,但据我了解,您不应该真正使用 GZIP 图像:https://webmasters.stackexchange.com/questions/8382/gzipped-images-is-it-worth/57590#57590

但有可能原始图像已经上传到 S3 GZIPPED。但是 gzip 可能会误导,因为为什么它适用于较小的图像呢?我们在 Cloudfront 中禁用了 GZIP。

我已为 Lamda@Edge Resize 函数提供最大资源 3GB 内存和 30 秒超时。这对于较大的图像来说还不够吗?

我已经删除了已经生成的图像,使 Cloudfront 无效,但它的行为仍然相同..

编辑:更新

我只是尝试了一个不同的图像然后它工作正常..我不知道为什么以及如何解决损坏的图像...我想 Cloudfront 现在已经缓存了 502..我已经无效使用了“*”但没有帮助。两个原始文件都是 jpg。

工作图像的原始源图像为 6.1 MB,非工作图像为 6.7 MB(如果重要的话)。

它们有以下限制: https://docs.aws.amazon.com/lambda/latest/dg/limits.html

response.body 停止工作时大约有 512 MB。

Lambda 中存在一些下限,尤其是 Lambda@Edge 中的响应大小。整个响应的限制为 1 MB,包括 headers 和 body。如果 lambda 函数 returns 更大的响应,它将被截断,这可能导致 HTTP 500 状态。参见 documentation

您可以通过将结果图像保存在 S3 上(或者可能首先检查它是否已经存在)来解决这个问题,然后不返回它,而是将 301 重定向到与该存储桶集成的 CloudFront 分发 - 因此图像请求将是重定向到结果图像。

例如在 node.js 中使用 Origin-Response 触发器:

'use strict';
exports.handler = (event, context, callback) => {
    // get response
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    // create image and save on S3, generate target_url
    // ...

    // modify response and headers
    response.status = 301;
    response.statusDescription = 'Moved Permanently';
    headers['Location'] = [{key: 'Location', value: target_url}]; 
    headers['x-reason'] = [{key: 'X-Reason', value: 'Generated.'}]; 

    // return modified response
    callback(null, response);
};

简单 Lambda 网关的版本(没有 Origin-Response,替换 headers):

exports.handler = (event, context, callback) => {
    // create image and save on S3, generate target_url
    // ...
    var response = {
      status: 301,
      headers: {
        Location: [{
          key: 'Location',
          value: [],
        }],
        'X-Reason': [{
          key: 'X-Reason',
          value: '',
        }],
      },
    };
    callback(null, response);
}

@Zbyszek 回答的附加说明,如果请求大于 1MB,您可以像这样粗略估计:

const isRequestBiggerThan1MB = (body, responseWithoutBody) => {
  const responseSizeWithoutBody = JSON.stringify(responseWithoutBody).length;
  return body.length + responseSizeWithoutBody >= 1000 * 1000;
};

responseWithoutBody 不能太大或包含 "recursive keys"(或它的名称),但在这种情况下,我无法想象您会拥有它。如果它包含递归键,那么您可以简单地删除它们。如果 responseWithoutBody 太大,您需要删除这些值并单独测量它们 - 例如,就像我在处理 response.body.