多人游戏无服务器 Matchmaker

本文将描述 GitHub 上的此示例中使用的体系结构。 请注意,此参考体系结构中的代码只是一个指南示例,在用于生产环境之前,可能还有需要优化的地方。

M使用 Azure Redis 缓存和 Azure Functions 的 Matchmaker

相关服务

  • Azure 流量管理器 - 选择此服务是因为它可以根据延迟情况将玩家连接到最合适的区域。
  • Azure Functions - 选择此服务是因为它是运行小部分匹配逻辑的最简单方法,此外,仅当您的用户尝试匹配和玩游戏时,才会向您收取费用。
  • Azure 事件中心 - 允许批处理请求,便于避免耗尽 Azure Redis 缓存连接池。
  • Azure Redis 缓存 - 简单的键/值对数据库,用于存储不同玩家尝试加入多人游戏会话的信息,以及来自所用游戏服务器的连接信息(IP/端口)。 选择此服务是因为它具有查询灵活性,索引、复制和移出数据没有严格要求,并且定价基于独有的存储容量。
  • 资源组 - 针对 Azure 流量管理器使用一个资源组,并针对每个匹配区域处理程序各使用一个资源组(例如,一个用于北美、一个用于欧洲、一个用于拉丁美洲、一个用于亚洲等)。

部署模板

单击下面的按钮,将项目部署到您的 Azure 订阅:

此操作将触发模板部署,即系统会将 azuredeploy.json ARM 模板文件部署到您的 Azure 订阅,从而创建必要的 Azure 资源。 这可能会在您的 Azure 帐户中产生相应费用。

请查看一般指南文档,其中有一部分概述了 Azure 服务的命名规则和限制。

备注

如果您对 ARM 模板的工作原理感兴趣,请参阅此参考体系结构中使用的每个不同服务对应的 Azure 资源管理器模板文档:

最后,添加 Function 应用程序设置,以便示例项目可以联系 Azure 服务:

  • EVENTHUB_CONNECTION_STRING - 所创建的 Azure 事件中心命名空间的连接字符串
  • REDIS_CONNECTION_STRING - 所创建的 Azure Redis 缓存的连接字符串

提示

要在本地运行 Azure Functions,请使用这些相同的应用设置更新 local.settings.json 文件。

分步操作

  1. 玩家的设备客户端连接到流量管理器,以传送要添加到匹配队列的玩家请求。
  2. 流量管理器连接到具有最低延迟的区域,并指向可捕获请求的 Azure 事件中心,允许对请求进行批处理。
  3. 事件中心传递新玩家的详细信息,以便将其添加到添加玩家 Azure Functions 中的匹配池。
  4. 此 Azure Functions 将首先创建一个匹配请求 Durable Azure Functions Orchestrator,该协调程序将公开一个结束点,以便通过轮询机制为玩家提供游戏服务器连接详细信息。 每个匹配请求都将有一个协调程序,并且协调程序的协调实例 ID 应为玩家 GUID,以便之后能够向该 ID 发送事件。
  5. 然后,同一 Azure Functions 将玩家添加到数据库中,并将他们包含在合适的现有游戏会话中。如果没有可用的游戏会话,它会创建一个。
  6. 在此之后,在轮询机制中,玩家的设备客户端连接到流量管理器以发送玩家请求,以便提供游戏会话连接详细信息。
  7. 流量管理器指向之前提到的 Azure Functions(第 4 步)。
  8. 该 Azure Functions 创建一个用于判断超时的计时器,然后等待包含游戏会话服务器连接详细信息的事件 (IP:Port) 或超时事件,以先发生的事件为准。
  9. 就绪会话计时器已触发 Azure Functions 扫描数据库,以获取具有足够玩家的任何会话并开始匹配。
  10. 该函数还会在用于获取服务器连接详细信息 (IP:Port) 的数据库中查找可用的服务器,并从可用服务器列表中将其删除。
  11. 对于每个可随时启动的游戏会话,就绪会话计时器已触发 Azure Functions 都将获取玩家列表,然后将该信息与服务器连接详细信息一起传递给 Azure 事件中心。
  12. 此 Azure 事件中心向 *匹配成功通知分发器 Azure Functions 传递数据。
  13. 此 Azure Functions 使用这些 Azure 事件中心消息,并将玩家 GUID 用作协调程序实例 ID,来联系各个玩家的匹配请求 Durable Azure Functions Orchestrator(第 6 步)。
  14. 设备客户端收到用于连接的服务器连接详细信息 (IP:Port),并直接连接到游戏服务器。 如果匹配尝试超时,设备客户端将慢慢处理连接。

实现详细信息

匹配请求监视器(每个匹配请求一个)

当玩家想要连接到游戏时,启动匹配请求 Durable Azure Functions Orchestrator 可执行以下任务:

  1. 调用 CreateTimer 创建请求的超时期限。
  2. 调用 WaitForExternalEvent 以等待 server ready 事件,其中包含服务器信息(请查看匹配成功通知分发器 Azure Functions)。
  3. 使用 Task.WhenAny 等待第一个任务返回,1 或 2。
  4. 如果超时在 server ready 事件到达之前发生,则返回设备客户端能够理解的某些失败消息。
  5. 如果 server ready 事件在超时之前到达:
    • 取消超时计时器。
    • 返回服务器信息。

注意:游戏的协调实例 ID 应为玩家 ID,或满足以下条件的请求的其他唯一标识符:a) 由设备客户端所有,以及 b) 存储在 Azure Redis 缓存中。

已批处理的就绪会话监视器(计时器触发的 Azure Functions)

它在 Azure Functions App 启动时启动:

  1. 在 Azure Redis 缓存中查询所有就绪会话。
  2. 对于每个就绪会话,发送 server ready 通知。

匹配成功通知分发器 Azure Functions

在通知系统触发的使用“server ready”通知的 Azure Functions(非 Durable)中:

  1. 向匹配成功通知分发器 Azure Functions 发送请求。
  2. 使用匹配请求 Durable Azure Functions Orchestrator 向会话中的每个玩家发送 server ready 事件。
    • 发送到与玩家 ID 匹配的协调程序实例 ID – 需要使用特定的协调实例 ID,此函数才会知道要将事件发送到的位置。
    • 发送服务器信息。

设备客户端会轮询匹配请求监视器的状态,看看系统是正在协调,还是已完成协调(即返回超时消息或服务器信息)。 如果返回服务器信息,它将连接到服务器。 对于本例,需要由设备客户端调用的具体 GET URL 为: GET /runtime/webhooks/durabletask/instances/{playerGUID}?code={systemKey}

读者练习

如果玩家决定取消正在进行的匹配请求,则提供的示例不包含处理这种情况的逻辑。 在这种情况下,应从数据库中删除玩家以实现未来的匹配尝试。

示例也不使用 PlayersTimeStartedMatchmaking 和 SessionsCreationTime 有序集,它们可以用来优化时间和匹配时间。

此外,如果没有可用的服务器,示例也不包含可横向扩展请求的代码来让请求变得尽可能通用。 批处理就绪会话监视器计时器触发的 Azure Functions 中有相应提示。

数据库设置

名称 Redis 类型 详细信息 Redis 命令示例
Servers Redis 哈希 存储服务器信息 hmset Servers GUID "IP:Port" GUID "IP:Port"
ServersAvailable Redis 集 存储可用的服务器 GUID,并可以托管游戏会话 sadd ServersAvailable "GUID"
Player Redis 哈希 存储玩家信息 hmset Player:GUID Name "value1" MatchmakingSettings "value2"
PlayersTimeStartedMatchmaking Redis 有序集 存储玩家的开始日期以查找匹配项。 密钥是玩家 GUID,即日期的 UNIX 时间戳值 zadd PlayersTimeStartedMatchmaking GUID TIMESTAMP
Session Redis 哈希 存储游戏会话信息,包括容量和匹配存储桶 hmset Session:GUID Capacity value1 MatchmakingSettings value2
SessionsPerMatchmaking Redis 集 存储每个匹配存储桶的会话 sadd SessionsPerMatchmaking:MatchmakingBucket "GUID"
SessionPlayers Redis 集 存储游戏会话中的玩家标识符 sadd SessionPlayers:SESSIONGUID "PLAYERGUID"
SessionsReady Redis 集 存储所有容量为零的会话,这意味着只要游戏服务器可用,就可以立即启动 sadd SessionsReady "GUID"
SessionsCreationTime Redis 有序集 存储每个游戏会话初始化时的日期 zadd SessionsCreationTime GUID TIMESTAMP

缩放

关于 Azure Redis 缓存容量,请参阅如何缩放 Azure Redis 缓存,了解如何适当缩放用于存储匹配信息的数据库。

请查看一般指南文档,了解 Azure 事件中心的独特之处以及为分区计数建模的经验法则。

其他资源和示例

适用于 Unity 的 Azure 事件中心 SDK:这是一个沙盒项目。 这篇文章中的内容不受支持,因此可能已过期或不处于工作状态。

定价

如果您没有 Azure 订阅,可以创建免费帐户,开始使用 12 个月的免费服务。 除非您超出这些服务的使用限制,否则无需为 Azure 免费帐户中包含的这些免费服务付费。 了解如何通过 Azure 门户使用情况文件查看服务使用情况。

您需要承担运行这些参考体系结构时使用的 Azure 服务的费用,总金额取决于将通过分析管道运行的事件数。 请参阅参考体系结构中使用的每项服务的定价网页:

您还可以使用 Azure 定价计算器,以配置和估算您计划使用的 Azure 服务的成本。