亚马逊AWS官方博客

利用 Lambda 与 SQS 实现动态补充 CloudFront 海外源站资源

1.背景和挑战

随着国内互联网媒体企业纷纷选择出海拓展业务,如何提高海外终端用户的用户体验成为各个企业重点关注内容,CloudFront是AWS的网络分发(CDN)服务,很多企业选择使用AWS S3服务作为静态内容持久存储源并利用CloudFront服务分布在全球各地的边缘站点进行资源分发,这样可以大大提高终端用户的使用体验。实际情况中,许多媒体企业海外业务并不完全独立于国内业务,例如:媒体制作,视频转码,内容合规性的检查等服务都依赖于国内资源。而不同的海外地区频繁访问的内容也不尽相同,所以通常国内源站存储全量资源,海外存在多个源站存储当地热点资源。那如何将国内源站的哪些资源同步到海外的哪个源站就成为一个必须解决的问题。面对这个问题需要从实际使用情况出发,真实的本地终端客户访问的资源才是需要存储到本地源站的资源。面对这种场景,出海企业需要一个解决方案能够根据真实客户需求动态将国内源站内容补充到海外源站中。

 

2.架构设计

  • 在海外AWS指定region部署S3作为主源站,存储该地区热点数据。
  • 在国内用户可以部署自己的源站应用服务。
  • 当海外用户访问某一些资源,在CloudFront缓存不存在且海外源站S3中也不存在此资源文件时,需要将该请求回源到国内源站,并同时触发一个Lambda@edge并行进行补充该资源文件到海外源站。
  • 为了不影响终端用户的使用体验,使用SQS将终端请求与补源操作进行解耦。在SQS消费端创建一个事件触发的Lambda处理函数,此函数进行资源的补齐。

整体结构设计和流程图如下所示。

 

3.部署和配置

环境准备:海外源站为在AWS外海指定区域部署的S3存储桶,用于持久化存储本地常访问的资源,使用ALB+WebApp作为国内全量源站。在本实验中这两个环境已经提前创建完毕。

  • S3存储桶信息:xinranl-static.s3.amazonaws.com
  • ALB信息:TestALB-XXXXXXX.elb.amazonaws.com

1)创建CloudFront分发,将S3存储桶作为默认源站。由于我们所创建的存储桶为私有的,所以需要在创建分发的时候需要有一个源访问身份。如果您之前已经创建过分发并且创建过源访问身份可以在这里复用,如果没有可以在这里选择创建新身份。在授予对存储桶的读取权限项选择“是,更新存储桶策略”。

当创建完成分配后,我们可以在该分配的“源和源组”中看到刚刚指定的源站。

2)创建全量源站,将ALB作为新的源站。

3)然后创建源站组,将S3作为主源站,将ALB作为备源站。将故障转移条件全部勾选上。

4)更新行为的“源与源组信”信息,在分配中的“行为“选项栏中,编辑行为,将源和源组配置为刚刚我们创建的源组。

5)创建SQS队列。创建标准SQS队列SyncSourceTaskQueue,记录队列信息。

6)创建Lambda触发器函数。为此函数添加触发器,当请求回源的时候才触发此函数。触发器配置选择CloudFront,分配选择刚刚我们创建的CloudFront分配,请求选择“源请求”。

该函数主要逻辑是:当一个请求没有在缓存中命中,请求会回到源站,此时会触发该函数。函数判断请求是回到哪个源站,如果回到S3源站此函数忽略处理,如果请求是回到ALB源站说明请求资源在S3源站中没有,需要触发补源流程,此函数将会把请求内容信息发送到SQS队列中,通知后续补源处理。此函数代码实现如下:

代码段:

var onlyhttp = require('http');

const fs = require('fs')

var AWS = require('aws-sdk');

var sqs = new AWS.SQS();

exports.handler = (event, context, callback) => {

console.log('event: ',JSON.stringify(event));

const response = event.Records[0].cf.response;

if (event.Records[0].cf.request.headers.host[0].value == 'xinranl-static.s3.amazonaws.com'){

callback(null, response);

}

var body = event.Records[0].cf.request.uri;

var params = {

MessageBody: body,

QueueUrl: 'https://sqs.us-east-1.amazonaws.com/XXXXX/Sync_source_queue'

};

sqs.sendMessage(params, function(err, data) {

if (err) console.log(err, err.stack); // an error occurred

else     console.log(data);           // successful response

});

callback(null, response);

};

7)Lambda补源函数,将本地源站不存在的内容从全量源站补充过去。创建Lambda函数,并为其增加触发器,触发配置选择SQS,SQS队列选择刚刚我们创建的队列ARN。

该函数主要逻辑是:SQS队列不为空的时候,队列里面每一个消息会触发一个Lambda函数。该Lambda函数会从SQS消息体得到要补充的资源信息,并发送获取请求到全量源站,之后再将获取到的数据发送到需要补充到的海外源站中。

代码段:

var onlyhttp = require('http');
const fs = require('fs')
var AWS = require('aws-sdk');
var sqs = new AWS.SQS();
exports.handler = (event, context, callback) => {
    console.log('event: ',JSON.stringify(event));
    const response = event.Records[0].cf.response;
    if (event.Records[0].cf.request.headers.host[0].value == 'xinranl-static.s3.amazonaws.com'){
        callback(null, response);
    }
    var body = event.Records[0].cf.request.uri;
    var params = {
        MessageBody: body,
        QueueUrl: 'https://sqs.us-east-1.amazonaws.com/XXXXX/Sync_source_queue'
    };
    sqs.sendMessage(params, function(err, data) {
        if (err) console.log(err, err.stack); // an error occurred
        else     console.log(data);           // successful response
    });
    callback(null, response);
};

 

4.验证步骤

  • 在自动补源前S3源站中并不存在测试资源images/shared/video.mp4

  • 用户可以直接通过ALB获取媒体资源,判断该资源是否真实存在。
  • 终端用户可以通过CloudFront分配的公网域名访问该媒体资源。此请求会触发自动补源流程。

d29f62eg8qfefw.cloudfront.net/images/shared/video.mp4

  • 补源流程完成时间跟网络状况与文件大小有关,在整个流程完成后,我们可以看到S3中已经出现最新补充的资源信息。

 

5.参考链接

[1]CloudFront开发人员指南:https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/Introduction.html

[2]SQS开发人员指南:https://docs.aws.amazon.com/zh_cn/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.htmlhttps://docs.aws.amazon.com/zh_cn/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html

[3]Lambda开发与部署:

https://docs.aws.amazon.com/zh_cn/lambda/latest/dg/getting-started-create-function.html

 

本篇作者

刘欣然

AWS解决方案架构师, 目前负责互联网媒体行业云端应用的架构设计与技术咨询。在加入AWS之前从事多年互联网开发工作,目前专注于Devops与边缘计算领域。