亚马逊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 游戏服务器上,你需要自定义路由加速器,本文介绍了如何通过自定义路由建立战斗服加速服务的一种实现方式。

整体架构和流程

本场景模拟玩家在游戏中发起一场对战的情况,整体的处理过程如下:

  1. 玩家在游戏中发起对战请求,发送到 API Gateway 后,转发到 GameLogic 的 Lambda。
  2. GameLogic 需要确定哪台服务器的负载较低,通过向 Amazon MemoryDB for Redis 查询负载最低的对战服务器。
  3. Amazon MemoryDB 通过对存储所有对战服务器的连接数量的 SortedSet 进行排序,查找到负载最低的服务器(对战服务器 IP 及端口)并返回给 GameLogic 的 Lambda。
  4. GameLogic 的 Lambda 根据返回的战斗服地址通过 ListCustomRoutingPortMappingsByDestination 接口向 Global Accelerator 查询 Listener 对应端口。
  5. AWS Global Accelerator 返回查询结果,即 AWS Global Accelerator 提供给玩家访问的端口。
  6. 玩家收到 AWS Global Accelerator 对外端口后,连接到 AWS Global Accelerator 的端口。
  7. AWS Global Accelerator 根据端口对应关系,把连接请求发送到子网中的特定的游戏对战服务器,也就是我们在步骤 3 中查找到的负载最低的对战服务器。
  8. 对战服务器根据游戏建立情况把对战服务器负载状态更新到 MemoryDB 中。
  9. (可选)此时如果某个玩家(例如 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 的自定义路由,其他服务器创建可以参考链接自行完成。

  1. 创建 Amazon MemoryDB for Redis,可以用来存储对战信息,也可用 SortedSet 来对负载服务器的排序。可以通过创建 MemoryDB for Redis 的 Cluster的教程一步一步创建。
  2. 创建负责游戏逻辑处理的 GameLogic 的 lambda,这部分逻辑包含几个部分:
    a. 向 MemoryDB 发出请求查找负载最低的对战服务器,这部分可以通过 SortedSet 来存储各服务器的负载。
    b. 根据对战服务器的地址向 AWS Global Accelerator 查找玩家可以访问的对外的端口。
    c. 把对战 session 信息存储到 MemoryDB 中,当断线重连时可以通过 session 信息找到匹配的对战服务器,在游戏结束时删除对战 session 信息。
  3. 创建 API Gateway 结合 Lambda 处理来自于前端的请求,可以通过创建 HTTP API 教程一步一步创建。
  4. 创建 VPC 以及私有子网,并部署游戏对战服务器。
  5. 创建自定义路由的 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.3 选择游戏服务器所在的区域,在这里我们使用的是“us-east-2”,EC2 实例对外提供两个端口 8080 和 8081,所以 端口选择 “端口范围起始值”是 8080,“端口范围结束值” 是 8081。
    • 5.4 选择端点组,即游戏服务器所在的子网,子网流量默认是“拒绝所有流量“,测试时可以选择“允许所有流量”,如果只想让该子网中的部分 EC2 接受来自于全局加速器的连接请求,可以选择“允许到特定目标套接字地址的流量”,在其中指定需要接受请求的 IP 和端口范围,最后点击创建 AWS Global Accelerator,等待部署结束就可以使用了。

测试验证

1. 通过以下命令可以查找到指定 EC2 内网 IP 的对外端口

aws globalaccelerator list-custom-routing-port-mappings-by-destination —destination-address 10.0.0.57 —endpoint-id subnet-xxxxxxxxxxxxxxxxxx —region us-west-2

可以看到 AWS Global Accelerator 的 Listener 对外端口 10010 指向子网中 IP 为 10.0.0.57 的 EC2 的 8080 端口。

2. 我们在 EC2 服务器上通过以下命令启动测试服务器,用来模拟一个游戏进程(MOBA 中对战房间):

注:此验证过程无需搭建本文中所述的完整游戏架构,只需构建 AWS Global Accelerator 和后端 EC2 部分即可。

cat << EoF > battleserver_demo.py
import socket
import time
import threading

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 8080))
s.listen(5)
print('Server is running...')
connections = []

def TCP(sock):
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if not data or data.decode('utf-8').replace('\n', '').replace('\r', '') == 'quit':
            Clean()
            break
        Battle(data)

def Clean():
    global connections
    for connection in connections:
        connection.close()
    connections = []


def Battle(data):
    for connection in connections:
        connection.send(data.decode('utf-8').upper().encode())


if __name__=="__main__":
    while True:
        sock, addr = s.accept()
        if len(connections) < 2:
            connections.append(sock)
            thread = threading.Thread(target=TCP, args=(sock,))
            thread.start()
EoF
nohup python3 battleserver_demo.py& 

3. 通过 Telnet 命令,分别在两台游戏客户端上通过 AWS Global Accelerator 暴露的对外端口连接到后端 EC2 游戏服务器。

telnet xxxxxxxx.awsglobalaccelerator.com 10010

4. 连接上 TCP 服务器后,模拟两台客户端上互发消息。

我们在测试机 1 发出“ping“消息,在测试机 1 和测试机 2 上都收到了 PING(测试服务器默认把所有消息大写)消息,可以验证测试机 1 和测试机 2 都成功连接到了内网 EC 的 8080 端口并能够进行消息交互。

通常每个游戏服上会运行多个游戏进程(在 MOBA 游戏中即为多个对战房间),本示例中从 AWS Global Accelerator 10010 映射到 EC2 的 8080 即为其中一个游戏进程,10011 映射到 8081 即为第二个游戏进程。如需要,您可以在更多的游戏服务器上运行上述 Python 程序进行进一步会话交互验证。

注意事项

在使用 AWS Global Accelerator 自定义路由的时候,请注意以下事项:

  1. 侦听器端口范围,侦听器端口范围需要足够多才能满足内网中 EC2 服务器处理需求(即文中提到的 100>32),否则会出现如下报错。
  1. 默认情况下,通过自定义路由加速器路由到子网中的 EC2 实例的流量都会被拒绝,要使目标实例能够接受流量,必须明确允许进入子网的所有流量,或者允许通过子网中的特定实例 IP 地址和端口,否则会出现连接不上的情况。
  2. 自定义路由不支持故障转移和健康检查,无论目标资源的运行状况如何,自定义路由加速器的流量都会以确定方式路由。
  3. 自定义路由加速器目前不提供 IPv6 的对外地址。
  4. 使用 aws cli 命令时需要指定区域 —region us-west-2

总结

本文从建立对战请求,到后端游戏会话管理,完整地介绍了如何利用 AWS Global Accelerator 的自定义路由功能来加速游戏会话,同时也对游戏重连逻辑进行了说明。 标准加速器适用于 4 层的无状态,或对会话粘性要求不高的业务场景进行加速;自定义路由加速器则可以根据您的业务特征,通过端口映射的方式来实现精准匹配和重连。针对不同的业务场景,可以选择不同的加速器来满足您的业务需求。

参考文档

https://docs.aws.amazon.com/zh_cn/global-accelerator/latest/dg/introduction-accelerator-types.html

https://aws.amazon.com/blogs/networking-and-content-delivery/introducing-aws-global-accelerator-custom-routing-accelerators/

https://aws.amazon.com/blogs/gametech/deploy-game-servers-with-amazon-gamelift-fleetiq-and-integrate-with-custom-routing-aws-global-accelerator/

本篇作者

何涛

AWS 技术客户经理,负责企业级游戏客户的架构和成本优化,拥有超过 10 年的游戏开发和运维经验,在加入 AWS 前曾创业及在 GLU/IBM 等公司工作,主要关注网络及容器等技术的发展。

胡亚光

AWS 资深技术客户经理,主要负责游戏客户的架构优化、成本管理、技术咨询等工作。拥有超过 10 年项目实施和客户支持经验。在加入 AWS 前曾就职于 Citrix,主要服务于大型金融类客户。