本方案通过与Amazon CloudFront Origin-Response和Amazon Lambda@Edge函数相结合,当Amazon CloudFront缓存文件不存在回源Amazon S3时,Amazon S3桶中文件不存在,Amazon Lambda@Edge中部署的Amazon Lambda函数根据Amazon S3请求返回状态码,调用图片生成接口生成图片,Amazon Lambda函数获取业务接口生成的图片后,上传图片到Amazon S3桶,然后返回图片到Amazon CloudFront最终呈现给用户。
本方案主要解决在全球区域从其他云平台上的对象存储服务OSS(Object Storage Service)迁移回源请求转发场景,通过结合Amazon CloudFront+Amazon Lambda@Edge,将现有业务迁移到亚马逊云科技。能够在不修改原有代码的情况下,轻松实现回源文件不存在时,调用业务接口生成文件。采用无服务架构,您无需维护基础资源,系统能自动根据业务弹性扩缩容。
需求背景
当客户业务需要请求者访问Bucket中不存在的文件(Object)时,根据回源规则指定的源站获取该文件。获取到目标文件后,会将文件返回给请求者并保存到Bucket中。
流程如下:
因Amazon S3不能直接配置外部接口调用(业务接口程序),需要通过Amazon CloudFront+Amazon Lambda@Edge方式来实现该功能。
解决方案架构
具体架构如下:
Amazon Lambda@Edge是Amazon CloudFront的一个功能,可让您在靠近应用程序用户的地方运行代码,从而提高性能,降低延迟。使用Amazon Lambda@Edge,您无需在全球多个地方预置或管理基础设施。您只需按使用的计算时间付费—代码未运行时不产生费用。
Amazon Lambda@Edge
https://aws.amazon.com/cn/lambda/edge/?nc1=h_ls
Amazon CloudFront
https://aws.amazon.com/cloudfront/
使用Amazon Lambda@Edge,您可以将Web应用程序分布在全球并提高它们的性能(并且无需管理任何服务器),从而丰富您的Web应用程序。Amazon Lambda@Edge 根据Amazon CloudFront内容分发网络(CDN)生成的事件运行代码。您只需将代码上传到Amazon Lambda无服务器计算环境,后者将在靠近最终用户的亚马逊云科技边缘站点完成运行和扩展代码所需的一切操作,从而实现高可用性。
对于Amazon CloudFront分配中的每个缓存行为,您最多可添加四个触发器(关联),以便在发生特定Amazon CloudFront事件时触发Amazon Lambda函数执行。Amazon CloudFront触发器可以基于四个Amazon CloudFront事件之一,如下图所示。
可用于触发Amazon Lambda@Edge函数的Amazon CloudFront事件如下:
查看器请求:当Amazon CloudFront收到查看器的请求时及它检查请求的对象是否在Amazon CloudFront缓存中之前,该函数会执行。
源请求:仅当Amazon CloudFront将请求转发给您的源时,该函数才会执行。当请求的对象位于Amazon CloudFront缓存中时,该函数不会执行。
源响应:在Amazon CloudFront收到来自源的响应之后及它将对象缓存在响应中之前,该函数会执行。请注意,即使从源返回了错误,该函数仍会执行。
在以下情况下该函数不会执行:
-
当请求的文件位于Amazon CloudFront缓存中并且未过期时;
当从由源请求事件触发的函数中生成响应时。
查看器响应:在将请求的文件返回到查看器之前,该函数会执行。请注意,无论文件是否已在Amazon CloudFront缓存中,该函数都会执行。
Amazon Lambda是一种无服务器的计算服务,让您无需预置或管理服务器、创建可感知工作负载的集群扩展逻辑、维护事件集成或管理运行时,即可运行代码。借助Amazon Lambda,您几乎可以为任何类型的应用程序或后端服务运行代码,而且完全无需管理。只需将您的代码以.zip文件或容器映像的形式上传,Amazon Lambda便会自动、精确地分配计算执行能力,并根据传入的请求或事件运行您的代码,以适应任何规模的流量。
您可以将您的代码设置为自动200多个亚马逊云科技服务和SaaS应用程序触发,或者直接从任何Web或移动应用程序调用。您可以使用自己喜欢的语言(Node.js、Python、Go、Java等)编写Amazon Lambda函数,并使用无服务器和容器工具(例如SAM或Docker CLI)来构建、测试和部署您的函数。
实施步骤
Amazon Lambda函数需要部署在us-east-1,因为要发送至Amazon CloudFront Edge只有us-east-1区域支持这项操作。
步骤1. 新建Amazon S3桶chivas-test-1
可以通Web控制台、Amazon CLI、Amazon SDK等方式创建存储桶。
步骤2. 编辑存储桶策略
{
"Version":"2012-10-17",
"Statement":[
{
"Principal":{
"AWS":"*"
},
"Effect":"Allow",
"Action":[
"s3:GetObject"
],
"Resource":"arn:aws:s3:::chivas-test-1/*"}
]
}
左右滑动查看完整示意
如下图所示:
步骤3. 创建Amazon CloudFront分配
创建分配,如图所示:
缓存行为配置,如下图所示:
关联函数配置,如下图所示:
其他配置完成后,点击创建分配按钮,完成分配创建。
步骤4. 创建Amazon Lambda函数
Amazon Lambda函数采用Node.js,代码如下:
index.js
const querystring = require("querystring");
const httpreq = require("request");
const AWS = require("aws-sdk");
const S3 = new AWS.S3({
signatureVersion: "v4"
});
const bucketName = " chivas-test-1";
const s3Basekey = "api/image/";
const backendAddress = "https://xxxxxxxx.com";
exports.handler = (event, context, callback) => {
let response = event.Records[0].cf.response;
if (response.status != 403) {
callback(null, response);
return;
}
let request = event.Records[0].cf.request;
//console.log('event', JSON.stringify(event))
let path = request.uri;
let key = decodeURIComponent(s3Basekey+path.substring(1));
console.log({ key });
httpreq({
url: backendAddress + path,
method: 'GET',
encoding: null
}, (err, getRespone, body) => {
if (!err && getRespone.statusCode === 200) {
const contentType = getRespone.headers['content-type']
S3.putObject({
Body: body,
Bucket: bucketName,
ContentType: contentType,
Key: key
}, (err, data) => {
if (err) {
console.log(err, err.stack);
}
else {
response.status = 200;
response.body = body.toString("base64");
response.bodyEncoding = "base64";
if (contentType)
response.headers["content-type"] = [{ key: "Content-Type", value: contentType }];
}
})
} else {
console.log('download error url', backendAddress + path);
}
});
callback(null, response);
};
左右滑动查看完整示意
上述代码中:
const bucketName=“xxxxx”;
const backendAddress=“https://xxxxxxxx.com”;
修改成自己业务中的存储桶名和业务接口地址。
创建函数,如下图所示:
上传代码,也可以直接在Amazon Lambda控制台编写代码,本文采用上传本地代码方式:
上传完成,并发布代码:
配置角色权限:
配置角色策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
左右滑动查看完整示意
配置完成后,如下图所示:
添加Amazon Lambda触发器:
部署到Amazon Lambda@Edge:
部署完成,查看版本信息,如下图所示:
部署完成后测试,当Amazon CloudFront回源到Amazon S3图片不存在时,Amazon Lambda@Edge会根据Amazon S3返回状态码进行业务接口调用,业务接口返回图片后,Amazon Lambda@Edge上传Amazon S3,返回图片到请求端,如下图所示:
总结
使用Amazon Lambda@Edge动态调用业务接口生成图片的两个主要好处是:
将Amazon CloudFront+Amazon Lambda@Edge+Amazon S3相结合,把现有业务迁移到亚马逊云科技,能在不修改原有代码的情况下,轻松实现业务目标。
通过Amazon Lambda无服务器架构来简化基础架构。
参考文档
https://aws.amazon.com/cn/cloudfront/getting-started/
https://docs.aws.amazon.com/zh_cn/lambda/latest/dg/lambda-edge.html
https://aws.amazon.com/cn/blogs/china/automatically-create-and-update-lambda-edge-in-cloudfront/
https://aws.amazon.com/cn/blogs/networking-and-content-delivery/resizing-images-with-amazon-cloudfront-lambdaedge-aws-cdn-blog/
https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html
https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-how-it-works-tutorial.html
本篇作者
李龙
亚马逊云科技解决方案架构师,负责基于亚马逊云科技的解决方案咨询和设计,Serverless TFC成员。特别关注教育和媒体领域的云服务和生成式AI运用。
任耀洲
亚马逊云科技解决方案架构师,负责企业客户应用在亚马逊云科技的架构咨询和设计。在微服务架构设计、数据库等领域有丰富的经验。
星标不迷路,开发更极速!
关注后记得星标「亚马逊云开发者」
听说,点完下面4个按钮
就不会碰到bug了!
点击阅读原文查看博客!获得更详细内容!