亚马逊AWS官方博客
使用 AWS Global Accelerator 的自定义路由为对战游戏加速
服务介绍
AWS Global Accelerator 是 AWS 的一项全球加速服务,通过提供的静态 IP 将玩家的流量路由到 AWS 在全球部署的边缘节点,流量通过监控良好、无拥塞的、冗余的 AWS 全球网络传输到终端节点。通过最大限度地利用 AWS 全球骨干网络,AWS Global Accelerator 可确保流量始终通过最佳网络路径进行路由。
AWS Global Accelerator 有 2 种类型,标准(Standard)加速器和自定义路由(Custom Routing)加速器,两者各有不同应用场景。
标准加速器面临的挑战
在自定义路由加速器出现之前,游戏业务中标准加速器广泛应用于一些无状态服务的加速,例如游戏玩家的信息、游戏公会信息获取以及修改、副本数据更新、战斗结果保存等业务处理,但是在占比更多的其他游戏业务场景中,标准加速器就显得力不从心,例如:
1. 有状态游戏的断线重连
1)标准加速器客户端关联性配置(Client affinity)为空的时候,标准加速器通过五元组属性(源 IP、源端口、目标 IP、目标端口、目标端口和协议)来选择哈希值,当源端口变化时不能保证断线重连时重新连接到同一台服务器,导致同一游戏房间中的玩家异常掉线,对玩家体验和游戏公平性均会造成较大影响。
2)客户端关联性配置为源 IP 时,使用二元组属性(源 IP 和目标 IP)来选择哈希值,然而当玩家的网络状态变差时,玩家可能会选择切换接入网络,玩家从新的运营商接入点获得更新的 IP,这时候玩家就没有办法再回到原来的服务器,导致存储在特定对战服务器内存中的游戏数据丢失。
2. 组队游戏
在游戏中会经常出现多人一同打 BOSS、刷装备,以及人与人之间相互对战/组队对战,例如 MOBA(Multiplayer Online Battle Arena)类游戏,这时候需要把所有参与组队的玩家连接到同一台服务器,由于标准加速器只能根据五元组或者二元组来选择哈希值,玩家只能被随机分配到后面的 EC2 服务器上,不能实现组队游戏玩法。
3. 对战服管理
当需要组队游戏时,通常的做法是:
1)中心服务器为组队的多个玩家分配一台 EC2 服务器,玩家通过公网直接连接到对战服务器 EC2,这种方式把游戏服务器暴露在公网是非常不安全的。
2)为每一台游戏服务器添加一个标准加速器,这对于流量不大的游戏可能是一种办法,当需要成千上万台游戏对战服务器时,需要的标准加速器以及 Anycast IP 的数量就远远不够,同时这种用法也存在极大的资源浪费。
自定义路由加速器发布后,以上的问题可以得到很好的解决,通过自定义路由可以将来自玩家的请求路由到内网的单台 EC2 服务器的特定的端口,这种机制为对战游戏匹配玩家、共同连接同一台游戏战斗服提供了可能。在此也列出了标准加速器和自定义路由加速器在用法上的一些区别,供大家对比参考。
标准加速器 | 自定义路由加速器 | |
路由 | 加速器自动选择最近健康的终端节点,可以使用 Traffic dials,endpoint 的权重以及 affinity 来管理流量 | 可以通过选择静态 IP+端口来路由到特定的 EC2 实例端口 |
终端节点 | ALB/NLB/EC2/EIP | VPC subnets(流量路由到子网中的 EC2 实例) |
协议选择 | 在 Listener 可以选择 TCP 或者 UDP | Endpoint Group 可以选择 TCP 和/或 UDP |
简而言之,如果要将用户路由到特定的 EC2 游戏服务器上,你需要自定义路由加速器,本文介绍了如何通过自定义路由建立战斗服加速服务的一种实现方式。
整体架构和流程
本场景模拟玩家在游戏中发起一场对战的情况,整体的处理过程如下:
- 玩家在游戏中发起对战请求,发送到 API Gateway 后,转发到 GameLogic 的 Lambda。
- GameLogic 需要确定哪台服务器的负载较低,通过向 Amazon MemoryDB for Redis 查询负载最低的对战服务器。
- Amazon MemoryDB 通过对存储所有对战服务器的连接数量的 SortedSet 进行排序,查找到负载最低的服务器(对战服务器 IP 及端口)并返回给 GameLogic 的 Lambda。
- GameLogic 的 Lambda 根据返回的战斗服地址通过 ListCustomRoutingPortMappingsByDestination 接口向 Global Accelerator 查询 Listener 对应端口。
- AWS Global Accelerator 返回查询结果,即 AWS Global Accelerator 提供给玩家访问的端口。
- 玩家收到 AWS Global Accelerator 对外端口后,连接到 AWS Global Accelerator 的端口。
- AWS Global Accelerator 根据端口对应关系,把连接请求发送到子网中的特定的游戏对战服务器,也就是我们在步骤 3 中查找到的负载最低的对战服务器。
- 对战服务器根据游戏建立情况把对战服务器负载状态更新到 MemoryDB 中。
- (可选)此时如果某个玩家(例如 PlayerA)掉线,PlayerA 会通过 API Gateway 重新发送游戏请求。GameLogic 通过查询 Amazon Memory DB for Redis 中的用户信息,发现 PlayerA 尚存在一个活动的对战记录,即会通过 ListCustomRoutingPortMappingsByDestination 重新获取与对战房间匹配的 AWS Global Accelerator 对外端口。玩家获取到 AWS Global Accelerator 对外端口后进行重连,即可回到对战房间继续进行游戏对战。
环境搭建步骤
在开始之前,请先确保您具有登录 AWS 控制台的账号,并具备 AWS Global Accelerator 的操作权限权限,您可以先登录控制台,选择合适的区域,本文的重点在于使用 AWS Global Accelerator 的自定义路由,其他服务器创建可以参考链接自行完成。
- 创建 Amazon MemoryDB for Redis,可以用来存储对战信息,也可用 SortedSet 来对负载服务器的排序。可以通过创建 MemoryDB for Redis 的 Cluster的教程一步一步创建。
- 创建负责游戏逻辑处理的 GameLogic 的 lambda,这部分逻辑包含几个部分:
a. 向 MemoryDB 发出请求查找负载最低的对战服务器,这部分可以通过 SortedSet 来存储各服务器的负载。
b. 根据对战服务器的地址向 AWS Global Accelerator 查找玩家可以访问的对外的端口。
c. 把对战 session 信息存储到 MemoryDB 中,当断线重连时可以通过 session 信息找到匹配的对战服务器,在游戏结束时删除对战 session 信息。 - 创建 API Gateway 结合 Lambda 处理来自于前端的请求,可以通过创建 HTTP API 教程一步一步创建。
- 创建 VPC 以及私有子网,并部署游戏对战服务器。
- 创建自定义路由的 AWS Global Accelerator,我们假设我们在 Subnet /28 子网中的游戏服务器开放的连接端口为 8080/8081 两个端口。
-
- 5.1 在名称中输入要创建的“加速器名称”以及选择“自定义路由”的加速器类型。
-
- 5.2 创建侦听器,侦听器是 AWS Global Accelerator 对外提供的端口,可以直接接受来自于玩家的连接。需要注意的是:AWS Global Accelerator 的侦听器端口的数量 > 后端 EC2 游戏服务器的数量*每台服务器提供的端口数量。
在这个案例中,端口 10000 到 10099,共 100 个端口,我们设定的子网是/28 大小的子网,子网中 EC2 服务器的数量最多 16 个,每台 EC2 服务器提供 8080/8081 的 2 个端口接受来自于 AWS Global Accelerator 过来的连接。那我们可以看到 100 > 32(16 * 2)。
- 5.2 创建侦听器,侦听器是 AWS Global Accelerator 对外提供的端口,可以直接接受来自于玩家的连接。需要注意的是:AWS Global Accelerator 的侦听器端口的数量 > 后端 EC2 游戏服务器的数量*每台服务器提供的端口数量。
-
- 5.3 选择游戏服务器所在的区域,在这里我们使用的是“us-east-2”,EC2 实例对外提供两个端口 8080 和 8081,所以 端口选择 “端口范围起始值”是 8080,“端口范围结束值” 是 8081。
-
- 5.4 选择端点组,即游戏服务器所在的子网,子网流量默认是“拒绝所有流量“,测试时可以选择“允许所有流量”,如果只想让该子网中的部分 EC2 接受来自于全局加速器的连接请求,可以选择“允许到特定目标套接字地址的流量”,在其中指定需要接受请求的 IP 和端口范围,最后点击创建 AWS Global Accelerator,等待部署结束就可以使用了。
测试验证
1. 通过以下命令可以查找到指定 EC2 内网 IP 的对外端口