Amazon CloudFront 部署小指南(五)- 使用 Amazon 边缘技术优化游戏内资源更新发布...

内容简介
游戏内资源包括玩家的装备/弹药/材料等素材,对游戏内资源的发布和更新是游戏运营商的一个常规业务流程,使用频率会十分高,所以游戏运营商希望该流程可以做到简化和可控。针对这个需求,我们设计了 3 个架构,面向游戏内资源更新发布的 3 个典型场景,来优化游戏内资源更新发布流程。3 个场景分别为:
游戏内资源灰度更新
游戏内资源分渠道投放
游戏内资源黑白名单
该 3 个架构围绕亚马逊云科技 Amazon CloudFront 服务 — CloudFront 边缘计算服务 — WAF 服务展开实现。
场景一:游戏内资源灰度更新
游戏在运营过程中会有资源更新的需求,用一个简单模型来描述:老的资源 A 需要替换成成新的资源 B。不过这里有一个痛点,B 资源在推广到所有 CDN 之前,需要内部或者小范围确认资源的正确性。因此需要一种方式来保证 B 资源哪怕在源端更新了,只有受限的访问;大部分的玩家还是访问的还是 CDN 缓存内的资源 A。等 B 资源验证通过后,才会通过 CDN 刷新推广到所有的节点。
这个需求实现的难点在于:在客户端不做任何修改的情况下,使用相同的 URL 将少量用户请求引导资源 B;资源 A 和资源 B 不能共享缓存,以免引起资源访问冲突。我们采用 CloudFront Continuous Deployment 来实现这个需求:

配置步骤如下:
创建 Staging Distribution
功能验证
调度流量
正式上线
CloudFront 持续部署功能在实现上比较直观,用户可以通过控制请求配比方式实现金丝雀式的资源更新;在成本上相比于部署 CloudFront 边缘脚本 CloudFront Function 会更低一些。
创建 Staging Distribution
▪ 在 CloudFront 生产 distribution 下,点击“create staging distribution”

▪ 可以输入 Staging Distribution 的描述,然后点击“Next”

▪ 创建一个新的源站。用户可以对应创建一个新的源站部署资源 B(使用相同目录结构);也可以在现有源站上将资源 B 部署到另外一个目录中,然后创建新的 Host 容器,并且在 DNS 服务器上创建指向新的 Host 容器的记录


*注意:我们在新的 origin 中改写了 origin path,使得客户端无需做变更。
▪ 流量配置选择“header-based ”比例先选择为 0

▪ 创建 Staging Distribution

▪ 至此 Staging Distribution 创建完毕,等待 10 分钟左右部署完毕

功能验证
可以使用命令行工具 curl 进行验证:
resource_b 下载:
curl -H 'aws-cf-cd-resource:b' -v -o /dev/null 'https://www.company.com/resource' 左滑查看更多
resource_a 下载:
curl -H -v -o /dev/null 'https://www.company.com/resource' 左滑查看更多
通过观察下载的文件特征如字节数或者 ETAG 判断是否下载了不同内容的资源对象。
调度流量
▪ 点击‘Edit Policy’

▪ 在弹出窗口中修改流量调度策略,调度 1%的流量进行灰度发布


正式上线
用户在源站正式将 Resource B 覆盖 Resource A 后,需要在 CloudFront 上进行缓存刷新,并删除 Staging Distribution


场景二:游戏内资源分渠道投放
游戏在运营过程中会有针对不同渠道投放不同资源的需求,用一个简单模型描述:对于资源 A 和资源 B,从不同渠道进来的玩家会看到不同的资源。这里的痛点为,客户需要针对头部某个字段或者 url 中某个关键字快速匹配对应的资源。
内容概述
在这个方案构建中,我们将利用到 CloudFront Function 检查请求头部的字段,并根据匹配到的字段进行回源 URL Path 替换,此方案的好处在于,URL Path Rewrite 对于最终用户是无感知的,符合不同渠道进来的玩家对于某个相同的资源(比如资源 A),回源获取到的内容将会不同。
配置步骤如下:
创建 CloudFront Function
创建 Behavior 匹配存在不同渠道的 URL Path
实验验证及结论
创建 CloudFront Function
在此需求中,不同渠道的所获取的内容不同,意味着在源站侧(如 S3)上的路径信息不同,如用户请求的文件路径统一为/sample.file,而不同渠道在源服务上所对应的文件真实路径为/a_vendor/sapme.file
或是/b_vendor/sample.file,我们可以根据不同渠道(携带不同的请求头信息),来对请求头值进行提取,并进行回源 URL 替换,具体 CloudFront Function 参考代码如下:
function handler(event) {// 获取请求头var request = event.request;var headers = event.request.headers;var originUrl = event.request.uri;// 检查 x-vendor-type 请求头是否存在if (headers['x-vendor-type']) {var targetObject = headers['x-vendor-type'].value//修改回源 URL Path event.request.uri = '/' + targetObject + originUrl;}return request;
} 左滑查看更多
*注:此处 header 的值不直接取而是先进行判断的原因是,某些客户端可能不会携带 x-vendor-type 这个头,如果不增加判断直接取值,那么 header 不存在的情况将会导致 CloudFront Function 报错,在您部署代码时也可以进行相应的考虑。
CloudFront Function 代码保存完毕后,我们可以在调试界面进行调试并观察效果:

提交调试信息后,可以看到回源路径成功被修改,如下图:

调试无误后对 CloudFront 代码进行部署:

创建 Behavior 匹配
存在不同渠道的 URL Path
此处,为了方便展示效果,我们在 CloudFront 中的 Behavior 根据需求,进行路径匹配,此处的匹配以“/abtest-demo*”进行展示。

由于此处仅做 URL 改写,不同渠道针对相同 URL 将会拿到不同的内容,此处您还需要在 Behavior 中针对不同的请求头进行缓存上的区分,您可以自定义 Cache Policy,并将 x-vendor-object 请求头作为 Cache Key 的一部分,以进行缓存区分:

自定义 Cache Policy 后,在 Behavior 中进行应用:

在 origin policy 中,需要将自定义的请求头进行透传,以便 CloudFront Function 可以进行请求头的检查,您可以自定义 Origin Policy,也可以选择 AllViewer 将自定义请求头进行透传。
由于该改写逻辑运行的 event type 是 Viewer Request,您需要在 Behavior 定义中在正确的 event 中应用上述部署的 CloudFront Function 逻辑。

实验验证及结论
在源服务侧,我们分别针对 A 和 B 渠道存放不同版本的文件,以供调试,此处示例文件对应关系如下:
渠道 A :a_vendor
渠道 B :b_vendor
携带对应的请求头,多次请求后并观测效果:
//在请求头中携带 A 渠道特殊标识,回源真实路径为/a_vendor/example.file
% curl -v http://cloudfront-function.xxxxxxx.com/example.file -H 'x-vendor-type: a_vendor' 2>&1 | egrep 'X-Cache|This content'
< X-Cache: Hit from cloudfront
This content is for a vendor!//在请求头中携带 B 渠道特殊标识,回源真实路径为/a_vendor/example.file
% curl -v http://cloudfront-function.xxxxxxx.com/example.file -H 'x-vendor-type: b_vendor' 2>&1 | egrep 'X-Cache|This content'
< X-Cache: Hit from cloudfront
This content is for b vendor!//在请求头中不携带渠道特殊标识,回源真实路径为/example.file
% curl -v http://cloudfront-function.xxxxxxx.com/example.file 2>&1 | egrep 'X-Cache|This content'
< X-Cache: Hit from cloudfront
This content is for general usage without vendor! 左滑查看更多
通过以上的演示,我们成功的验证了以下三个设置需求:
您可以清楚的看到,针对相同的 URL path 为/example.file,通过不同的 x-vendor-type 指定内容,我们成功的改写了回源 URL,并根据获取到了不同渠道所对应的内容。
如果客户端不是通过渠道发起的请求,不携带特殊标识头,也可以成功拿到内容。
在命中缓存的情况下,由于 Cache Policy 将 x-vendor-type 作为缓存的一部分,在 URL 相同的情况下,请求依然可以按照预期命中到各自对应缓存。
场景三:游戏内资源黑白名单
场景描述
游戏在运营过程中会有不同玩家有不同访问行为的需求,用一个简单模型描述:对于玩家 A 需要放在白名单中,对于玩家 B 需要放置在黑名单中。
用一个更具体的场景描述:假设需要通过 WAF web ACL 去匹配出 user-agent 中包含 “PostmanRuntime/x.xx.x” 的请求,并且拒绝该请求的访问。
对于上述场景,可以通过检查请求 header 重的 User-Agent 来实现。对于请求来说,通常会经过几个链路:客户端→公网→Amazon CDN 接入点→ 原站。那么将检查 header 的压力放在 CDN 处是比较好的选择,可以有效的减少原站的压力。在 CDN 侧检测也有几种方式。最先想到的是在 CDN 上用 edge function/lambda 来实现。这里会带来的挑战是需要是维护代码。另外一种比较好的方式是利用 WAF 对于规则来进行检查。WAF 在配置 Web ACL 的时候,需要配置允许和阻止的条件。通常包含的条件有:
请求是否表现为包含恶意脚本允许或阻止请求 → 跨站点脚本匹配条件
请求源自的 IP 地址允许或阻止请求 →IP 匹配条件
请求源自的国家/地区允许或阻止请求 → 地理匹配条件
请求是否超过指定长度允许或阻止请求 → 大小约束条件
请求是否表现为包含恶意 SQL 代码允许或阻止请求 →SQL 注入匹配条件
出现在请求中的字符串允许或阻止请求 →字符串匹配条件
出现在请求中的正则表达式模式允许或阻止请求 → 正则表达式匹配条件
默认情况下,Amazon WAF 过滤器不检查 HTTP 请求参数是否存在。要检查 HTTP 请求参数是否存在,可以采取下面两种自定义规则中的任意一种:
使用字符串匹配规则
使用正则表达式匹配规则
环境准备
在测试之前我们需要做如下步骤:
1. 进入 Amazon Website Service Console 界面,选择 WAF 服务,然后在左边导航栏选择 WAF ACLs。
2. 创建一个 Rule,根据业务需求命名。然后保护的对象可以是 CDN 也可以是负载均衡器之类的对外服务。资源部分可以现在就填写,也可以等待 Rule 创建完毕后再绑定。

3. 完成 Web ACL 的信息录入之后,在第二部我们选择自定义规则。

4. 在自定义规则中,我们选择 Rule Builder。并且设置好 Rule 的名字。
5. 在 inspect 中,选择 All headers,header content 选择默认的所有 keys and values,当然可以根据业务需求只监控特定的 header 和 content。

字符串匹配规则
对于字符串匹配规则,Web ACL 提供如下几种方式:
Exact matches string:严格匹配字符串
Start swith string:匹配字符串开头
Ends with string:匹配字符串结尾
Contains string/word:包含某个字符串
根据需求,使用 Start with string 即可,设置如下:

正确表达式匹配规则
正则表达式规则,Web ACL 提供两种匹配方式:
▪ regex pattern set:正则表达式集合,可以提前设置,在面板中选取即可
▪ regular expression:正则表达式,临时设置
这里我们选择第二种方式,其设置参考如下

最终规则的 Json 格式参考
{"Name": "StringMatch","Priority": 0,"Action": {"Block": {}},"VisibilityConfig": {"SampledRequestsEnabled": true,"CloudWatchMetricsEnabled": true,"MetricName": "StringMatch"},"Statement": {"OrStatement": {"Statements": [{"ByteMatchStatement": {"FieldToMatch": {"Headers": {"MatchScope": "ALL","MatchPattern": {"All": {}},"OversizeHandling": "CONTINUE"}},"PositionalConstraint": "STARTS_WITH","SearchString": "PostmanRuntime","TextTransformations": [{"Type": "NONE","Priority": 0}]}},{"RegexMatchStatement": {"FieldToMatch": {"Headers": {"MatchScope": "ALL","MatchPattern": {"All": {}},"OversizeHandling": "CONTINUE"}},"TextTransformations": [{"Type": "NONE","Priority": 0}],"RegexString": "/PostmanRuntime[ \\/]+([0-9\\.]+)/gi"}}]}}
} 左滑查看更多
规则测试
规则建立好后,我们需要将它运用在保护的资源中。假设源站是一个网站,前面为一个 CDN,它只是返回一个 text=ok。
测试如下(User-Agent 的值为 X-UnrealEngine-Agent):

当请求头,包含了
PostmanRuntime/x.xx.x,可以看到请求被 block

在后台的界面中,也可以统计到过去 5 分钟的 WAF 的表现情况

可以通过以上实验,WAF 对于 CDN 的保护提供了十分灵活的配置规则,可以满足各种不同场景的需求。更进一步,它解放了 CDN 去做规则匹配和检查的压力,让 CDN 去做它更擅长的业务。
总结
通过亚马逊云科技 CloudFront 服务 — CloudFront 边缘计算服务 — WAF 服务,游戏运营商可以在不改动客户端的情况下,完成游戏内资源在预设条件下的更新发布,提高了业务部署的敏捷性和简易性。
亚马逊云科技 CloudFront
部署小指南系列文章
Amazon CloudFront 部署小指南(一)- 快速构建 CDN 内容分发:
https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-one/
Amazon CloudFront 部署小指南 (二)- 进阶部署:
https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-two/
Amazon CloudFront 部署小指南 (三)- 持续部署:
https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-three/
Amazon CloudFront 部署小指南(四)- CloudFront Function 基础与诊断:
https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-four/
Amazon CloudFront 部署小指南(六)- Lambda@Edge 基础与诊断:
https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-six/
本篇作者

叶明
亚马逊云科技边缘产品架构师,负责 CloudFront 和 Global Accelerator 服务在中国和全球的市场拓展,专注于互联网用户访问云上服务的感受的优化以及数据洞察。在互联网基础设施领域有丰富的实践经验。

万曦
亚马逊云科技解决方案架构师,负责基于亚马逊云科技的云计算方案的咨询和架构设计。坚实的 Amazon Builder 文化拥抱者。拥有超过 12 年的游戏研发经验,参与过数个游戏项目的管理和开发,对于游戏行业有深度理解和见解。

王骏兴
亚马逊云科技边缘产品架构师,负责亚马逊云科技 Edge 服务领域在中国的技术推广。在 CDN 内容分发以及 WAF 领域拥有多年实战经验,专注于边缘服务设计以及体验优化。


听说,点完下面4个按钮
就不会碰到bug了!

相关文章:
Amazon CloudFront 部署小指南(五)- 使用 Amazon 边缘技术优化游戏内资源更新发布...
内容简介 游戏内资源包括玩家的装备/弹药/材料等素材,对游戏内资源的发布和更新是游戏运营商的一个常规业务流程,使用频率会十分高,所以游戏运营商希望该流程可以做到简化和可控。针对这个需求,我们设计了 3 个架构,面…...
undefined reference to `dlopen‘ ‘SSL_library_init‘ `X509_certificate_type‘
使用Crow的时候需要注意crow依赖asio依赖OpenSSL,asio要求1.22以上版本,我使用的是1.26.0; 这个版本的asio要求OpenSSL是1.0.2,其他版本我得机器上编不过,ubuntu上默认带的OpenSSL是1.1.1; 所以我下载了OPENSSL1.2.0重…...
DHCPv6之GitHub项目Android侧验证
一、adb里面安装busybox 1、下载busybox 下载网址:Index of /downloads/binaries/1.21.1 (busybox.net),目前最新是1.21.1版本 根据项目选择busybox-armv7l ,右键另存为下载到本地目录,下载后去掉文件的后缀名,变成如…...
简单易懂的 Postman Runner 参数自增教程
目录 什么是 Postman Runner? Postman Runner 如何实现参数自增? 步骤一:设置全局参数 步骤二:将全局参数带入请求参数 步骤三:实现参数自增 资料获取方法 什么是 Postman Runner? Postman Runner 是…...
BeanFactory与Applicationcontext(1)
BeanFactory是接口,提供了IOC容器最基本的形式,给具体的IOC容器的实现提供了规范。BeanFactory是spring的“心脏”,核心容器,它也是Applicationcontext的父接口。 BeanFactory实质上并未提供过多的方法,spring容器的I…...
C++初阶之模板深化讲解
模板深化讲解 非类型模板模板的特化1.函数模板特化2.类模板特化 模板分离编译1.什么是分离编译2.模板的分离编译 模板总结 非类型模板 非类型模板(Non-Type Template)是 C 中的一种模板形式,它允许你在模板中传递除了类型以外的其他值&#x…...
Redis数据结构——整数集合
定义 整数集合是集合的实现方式之一,当一个集合只包含整数值元素时,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合的底层实现。 整数集合就是存放整数的一个数组,整数集合的结构体定义: typeof struc…...
背上大书包准备面试之CSS篇
目录 H5 新特性 css3新特性? 为什么要初始化css样式? 浏览器兼容性问题? css sprites(css精灵图)? css盒模型是什么样的? 页面中一个块元素的宽度包含了盒模型中的哪些部分?…...
linux系列基本介绍
虽然我们常说Linux操作系统,这种叫法是不正确的,严格意义上讲,Linux并不是操作系统,而是属于操作系统的一个内核,inux内核提供了操作系统的核心功能,如进程管理、内存管理、文件系统等。 Linux有很多不同的…...
vue.draggable浅尝
介绍 Vue.Draggable是一款基于Sortable.js实现的vue拖拽插件。支持移动设备、拖拽和选择文本、智能滚动,可以在不同列表间拖拽、不依赖jQuery为基础、vue 2过渡动画兼容、支持撤销操作,总之是一款非常优秀的vue拖拽组件。本篇将介绍如何搭建环境及简单的…...
Tree相关
1.树相关题目 1.1 二叉树的中序遍历(简单):递归 题目:使用中序遍历二叉树 思想:按照访问左子树——根节点——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候我们按照同样的方式遍历࿰…...
git日常操作-案例
文章目录 查看远程有那些分支 查看远程有那些分支 git ls-remote --heads origin...
cmake链接.lib库
当使用CMake编写CMakeLists.txt文件来链接其他文件夹的.lib库文件时,你需要做以下几个步骤: 设置库文件路径: 使用 link_directories() 函数设置要搜索库文件的路径。例如: link_directories(${CMAKE_SOURCE_DIR}/path/to/libra…...
SkyWalking 部署(包含ES)
SkyWalking安装 结构 首先SkyWalking主要需要oapService、webApp、Elasticsearch(可选存储)三个,接下来讲一下这三个的安装步骤,安装过程中出现了一些细小的配置错误,导致用了快两天才弄好,麻木了&#x…...
Android学习之路(2) 设置视图
一、设置视图宽高 在Android开发中,可以使用LayoutParams类来设置视图(View)的宽度和高度。LayoutParams是一个用于布局的参数类,用于指定视图在父容器中的位置和大小。 下面是设置视图宽度和高度的示例代码: …...
SIP/VoIP之常见的视频问题
除了语音通话外,视频通话也是SIP协议通话中重要的功能,在实际应用中,经常会遇到一些视频问题,如下(以h264为例) 一、 己方未显示对方视频图像 排查方法: 查看网络抓包中有没有发给已方的视频…...
docker的服务/容器缺少vim问题
背景/问题: docker的服务/容器缺少vim问题 bash: vim: command not found 在docker的mysql服务中安装Vim 1、执行apt-get update root6d8d17e320a0:/# apt-get update问题:文件下载失败 Err:1 http://security.debian.org/debian-security buster/updates InRe…...
HCIP-OpenStack
1、OpenStack概述 OpenStack是一种云操作系统,OpenStack是虚拟机、裸金属和容器的云基础架构。可控制整个数据中心的大型计算、存储和网络资源池,所有资源都通过API或Web界面进行管理。 为什么称OpenStack是云操作系统? 云一般指云计算&…...
前端:Vue.js学习
前端:Vue.js学习 1. 第一个Vue程序2. Vue指令2.1 v-if、v-else-if、v-else2.2 v-for2.3 事件绑定 v-on:2.4 v-model 数据双向绑定2.5 v-bind 绑定属性 3. Vue组件4. Vue axios异步通信5. 计算属性6. 插槽 slots7. 自定义事件内容分发 1. 第一个Vue程序 首先把vue.js拷贝到本地…...
MySQL到Oracle快速上手
第一次做Oracle项目的时候对一些语法区别不太清楚,这里列出一些开发中发现的与MYSQL不同的点 一个用户相当于一个数据库 表空间 表空间是用于存储表、索引和其他数据库对象的逻辑存储结构。每个表空间由一个或多个数据文件组成,这些文件可以位于不同的物…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
