低延迟,大规模的消息队列

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

假设我要在现有的开放协议的基础上构build一些东西,我想为100万个同时在线的玩家提供服务,只是为了解决这个问题。

假设每个玩家有一个传入的消息队列(对于聊天和什么),平均多一个传入消息队列(公会,区域,实例,拍卖等),所以我们有200万个队列。 一个玩家一次听1-10个队列。 每个队列平均每秒钟可能有1条消息,但某些队列的速率会更高,并且听众数量也会更高(比如说,一个实例的“实体位置”队列)。 我们假设不超过100毫秒的系统排队等待时间,这适用于轻度动作型游戏(但不包括Quake或Unreal Tournament等游戏)。

从其他系统,我知道在单个1U或刀片服务器上为10,000个用户提供服务是一个合理的预期(假设没有任何其他昂贵的事情发生,如物理仿真或其他)。

因此,在客户端连接到连接网关(又连接到消息队列服务器)的交叉开关集群系统中,每个网关有100个网关机器,每个网关有100个队列机器,每个队列服务器有20000个消息队列。 再次,只是为了一般的范围。 每台MQ机器上的连接数量很less:大约100个,可以与每个网关通信。 网关上的连接数量会更多:客户端的连接数量为10,100,连接到所有的队列服务器。 (最重要的是,为游戏世界模拟服务器添加一些连接,或者不pipe怎样,但是我现在试图保持这种分离)

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

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

@MSalters

Re'消息队列':

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

如果你想保证最终交付和其他持久性和恢复function,那么你也可以有这个 – 这是一个选项。 这就是RabbitMQ和AMQP的重点 – 只需一个消息传递系统就可以拥有很多行为。

您所描述的模型是DEFAULT行为,它是短暂的,“火和遗忘”,并将消息路由到收件人的任何位置。 正因为如此,人们使用RabbitMQ在EC2上进行多播发现。 您可以通过单播TCP pubsub获取UDPtypes的行为。 整洁,嗯?

重新UDP:

我不确定UDP是否会在这里有用。 如果closuresNagling,则RabbitMQ单消息往返延迟(client-broker-client)在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 / resendpipe理器,那么这可能会让你再次变慢。 这一切都取决于你的用例。 如果你真的真的需要使用UDP和lazy ack等,那么你可以去掉RabbitMQ的TCP,并可能把它closures。

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

其实我现在正在build立这样一个系统。

我对包括RabbitMQ,Qpid和ZeroMQ在内的多个MQ进行了相当数量的评估。 这些types的应用程序的延迟和吞吐量都足够了。 然而,不好的是队列创build时间在五十万或更多的队列中。 在几千人的排队之后,Qpid尤其严重降低。 为了避免这个问题,通常你必须创build自己的路由机制(总队列数量较less,这些队列上的消费者正在获取他们不感兴趣的消息)。

我目前的系统可能会使用ZeroMQ,但在集群内部的使用相当有限。 来自客户端的连接使用自定义sim进行处理。 我使用libev构build的守护进程,完全是单线程的(并且显示出非常好的扩展性 – 它应该能够在一个盒子上处理50,000个连接而没有任何问题 – 尽pipe如此,我们的sim卡速率是相当低的没有物理)。

XML(因此XMPP)非常不适合这一点,因为在绑定到I / O之前,你会紧紧跟踪CPU处理XML,而这不是你想要的。 目前我们正在使用Google协议缓冲区,而且这些缓冲区似乎非常适合我们的特殊需求。 我们也使用TCP进行客户端连接。 过去我曾经使用过UDP和TCP两种方式,正如其他人所指出的那样,UDP确实有一些优势,但要处理起来有点难度。

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

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

我不确定你为什么说AMQP用户都是大型企业型公司。 我们有一半以上的客户处于“networking”空间,从巨大的公司到小公司。 RabbitMQ已经构build了大量游戏,投注系统,聊天系统,twittery型系统和云计算基础架构。 甚至还有手机应用程序。 工作stream只是许多用例中的一个。

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

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

请看看。 我们在这里帮忙。 请随时给我们发电子邮件info@rabbitmq.com或在Twitter上打我(@monadic)。

FWIW,对于中间结果不重要的情况(如定位信息),Qpid有一个“最后一个值队列”,它只能向用户提供最近的值。

我的经验是一个非开放的替代品,BizTalk。 我们学到的最痛苦的教训是这些复杂的系统并不快。 正如你从硬件需求中得出的结论,这直接转化为巨大的成本。

出于这个原因,甚至不要靠近XML核心接口。 您的服务器群集将每秒parsing200万条消息。 这很容易就是2-20 GB /秒的XML! 然而,大多数消息将是几个队列,而大多数队列实际上是低stream量的。

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

另外,出于类似的原因,不要认为消息队列体系结构对于您的应用程序所需的所有通信需求都是最好的。 以您的“实例位置”为例。 这是您希望保证消息传递的经典案例。 你需要分享这些信息的原因是因为它一直在变化。 所以,如果一条消息丢失,你不想花时间恢复它。 您只能发送受影响实体的旧位置。 相反,您希望发送该实体的当前位置。 技术上,这意味着你需要UDP,而不是TCP和自定义丢失恢复机制。