44

我正在重新思考 Facebook 应用程序和云计算时代的大型多人游戏。

假设我要在现有的开放协议之上构建一些东西,并且我想同时为 1,000,000 名玩家提供服务,只是为了确定问题的范围。

假设每个玩家都有一个传入消息队列(用于聊天等),平均还有一个传入消息队列(公会、区域、实例、拍卖......),所以我们有 2,000,000 个队列。一名玩家一次将收听 1-10 个队列。每个队列平均每秒可能有 1 条消息,但某些队列将具有更高的速率和更多的侦听器(例如,级别实例的“实体位置”队列)。让我们假设系统排队延迟不超过 100 毫秒,这对于轻度面向动作的游戏(但对于 Quake 或 Unreal Tournament 之类的游戏)来说是可以的。

从其他系统来看,我知道在单个 1U 或刀片服务器上为 10,000 个用户提供服务是一个合理的期望(假设没有其他昂贵的东西,比如物理模拟或诸如此类的东西)。

因此,对于交叉开关集群系统,客户端连接到连接网关,连接网关又连接到消息队列服务器,我们将获得每个网关 10,000 个用户和 100 个网关机器,每个队列服务器有 20,000 个消息队列和 100 个队列机器。同样,仅用于一般范围。每台 MQ 机器上的连接数量很少:大约 100 个,用于与每个网关通信。网关上的连接数会高很多:客户端 10,100 + 到所有队列服务器的连接。(最重要的是,为游戏世界模拟服务器或其他东西添加一些连接,但我现在正试图将其分开)

如果我不想从头开始构建它,我将不得不使用一些现有的消息传递和/或排队基础设施。我能找到的两个开放协议是 AMQP 和 XMPP。XMPP 的预期用途有点像这个游戏系统所需要的,但是开销非常明显(XML,加上详细的存在数据,加上必须在上面构建的各种其他通道)。AMQP 的实际数据模型与我上面描述的更接近,但所有用户似乎都是大型企业型公司,工作负载似乎与工作流相关,而不是实时游戏更新相关。

有没有人有这些技术或其实现的白天经验,你可以分享?

4

5 回答 5

11

实际上,我现在正在构建这样一个系统。

我对几个 MQ 进行了大量评估,包括 RabbitMQ、Qpid 和 ZeroMQ。其中任何一个的延迟和吞吐量对于此类应用程序来说都绰绰有余。然而,不好的是在 50 万个或更多队列中的队列创建时间。在几千个队列之后,Qpid 特别严重地退化。为了规避这个问题,您通常必须创建自己的路由机制(队列总数较少,并且这些队列上的消费者正在获取他们不感兴趣的消息)。

我当前的系统可能会在集群内部使用 ZeroMQ,但方式相当有限。来自客户端的连接由自定义 sim 处理。我使用 libev 构建并且完全是单线程的守护程序(并且显示出非常好的扩展性——它应该能够在一个盒子上处理 50,000 个连接而没有任何问题——我们的 sim.tick rate 非常低,但是有没有物理)。

XML(以及因此 XMPP)非常不适合这种情况,因为您将在受 I/O 约束之前很久就锁定 CPU 处理 XML,这不是您想要的。目前,我们正在使用 Google Protocol Buffers,它们似乎非常适合我们的特殊需求。我们还使用 TCP 进行客户端连接。过去我曾有过同时使用 UDP 和 TCP 的经验,正如其他人所指出的,UDP 确实有一些优势,但使用起来稍微困难一些。

希望当我们更接近发布时,我将能够分享更多细节。

于 2010-02-03T18:15:41.483 回答
11

@MSalters

重新“消息队列”:

RabbitMQ 的默认操作正是您所描述的:transient pubsub。但是使用 TCP 而不是 UDP。

如果您想要有保证的最终交付以及其他持久性和恢复功能,那么您也可以拥有它 - 这是一种选择。这就是 RabbitMQ 和 AMQP 的全部意义——你可以通过一个消息传递系统来实现很多行为。

您描述的模型是默认行为,它是瞬态的,“即发即弃”,并将消息路由到收件人所在的任何地方。正是出于这个原因,人们使用 RabbitMQ 在 EC2 上进行多播发现。您可以通过单播 TCP pubsub 获得 UDP 类型的行为。整齐吧?

重新UDP:

我不确定UDP在这里是否有用。如果您关闭 Nagling,那么 RabbitMQ 单消息往返延迟(客户端-代理-客户端)的测量值为 250-300 微秒。请参阅此处与 Windows 延迟(稍高)进行比较http://old.nabble.com/High%28er%29-latency-with-1.5.1--p21663105.html

我想不出有多少多人游戏需要低于 300 微秒的往返延迟。使用 TCP 可以达到 300us 以下。TCP 窗口比原始 UDP 更昂贵,但如果您使用 UDP 更快,并添加自定义丢失恢复或 seqno/ack/resend 管理器,那么这可能会再次减慢您的速度。这完全取决于您的用例。如果你真的真的需要使用 UDP 和惰性应答等,那么你可以去掉 RabbitMQ 的 TCP 并可能将其取消。

我希望这有助于澄清为什么我为 Jon 的用例推荐 RabbitMQ。

于 2009-12-18T20:23:09.823 回答
5

Jon,这听起来像是 AMQP 和 RabbitMQ 的理想用例。

我不知道你为什么说 AMQP 用户都是大型企业类型的公司。我们一半以上的客户都在“网络”领域,从大公司到小公司。许多游戏、投注系统、聊天系统、twittery 类型系统和云计算基础设施都是基于 RabbitMQ 构建的。甚至还有手机应用程序。工作流只是众多用例之一。

我们试图跟踪这里发生的事情:

http://www.rabbitmq.com/how.html(确保您也点击进入 del.icio.us 上的用例列表!)

请看一看。我们是来帮忙的。请随时通过 info@rabbitmq.com 给我们发送电子邮件或在 Twitter (@monadic) 上联系我。

于 2009-12-18T14:01:40.390 回答
3

我的经验是使用非开放替代方案 BizTalk。我们学到的最痛苦的教训是这些复杂的系统并不快。正如您从硬件要求中得出的那样,这直接转化为可观的成本。

出于这个原因,甚至不要在核心接口上使用 XML。您的服务器集群每秒将解析 200 万条消息。这很容易达到 2-20 GB/秒的 XML!但是,大多数消息将用于少数队列,而大多数队列实际上是低流量的。

因此,设计您的体系结构,以便轻松地从 COTS 队列服务器开始,然后在识别出瓶颈时将每个队列(类型)移动到自定义队列服务器。

此外,出于类似的原因,不要假设消息队列架构最适合您的应用程序的所有通信需求。以您的“实例中的实体位置”为例。这是一个经典案例,您希望有保证的消息传递。您需要共享此信息的原因是因为它一直在变化。因此,如果消息丢失,您不想花时间恢复它。您只会发送受影响实体的旧位置。相反,您希望发送该实体的当前位置。从技术上讲,这意味着您需要 UDP,而不是 TCP 和自定义丢失恢复机制。

于 2009-12-18T14:38:26.647 回答
3

FWIW,对于中间结果不重要的情况(如定位信息),Qpid 有一个“最后值队列”,它只能将最新值传递给订阅者。

于 2009-12-29T17:37:43.077 回答