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

45

在Facebook应用和云计算时代,我正在重新思考大规模多人游戏。假设我要在现有开放协议的基础上构建一个服务于100万并发玩家的系统。

假设每个玩家都有一个传入消息队列(用于聊天等),平均还有一个传入消息队列(公会、区域、副本、拍卖等)。因此我们需要2,000,000条队列。每个玩家会同时监听1-10个队列。每个队列平均可能每秒接收到1条消息,但某些队列将具有更高的速率和更多的听众(例如,关于某个等级副本中实体位置的队列)。让我们假设系统排队延迟不超过100毫秒,这对于轻度动作取向的游戏来说是可以接受的,但对于Quake或虚幻锦标赛之类的游戏则不行。

从其他系统中,我知道在单个1U或刀片机箱上为10,000个用户提供服务是可以合理期望的(假设没有其他昂贵的操作,如物理模拟等)。

因此,使用交叉栏群集系统,客户端连接到连接网关,然后连接到消息队列服务器,我们将获得每个网关10000个用户,使用100个网关机器,每个队列服务器20000个消息队列,使用100个队列机器。再次强调,这只是一般范围的估计。每个MQ机器上的连接数量非常少:约为100个,用于与每个网关通信。网关上的连接数量将要高得多:10,100个用于客户端和所有队列服务器的连接。(除此之外,还需要添加一些用于游戏世界模拟服务器等的连接,但我现在试图将其分开处理)

如果我不想从头开始构建,那么我必须使用一些现有的消息传递和/或队列基础设施。我能找到的两个开放协议是AMQP和XMPP。 XMPP的预期用途更类似于这个游戏系统所需的,但开销非常明显(XML,加上冗长的存在数据,加上必须在其上构建的各种其他通道)。 AMQP的实际数据模型更接近我上面描述的内容,但所有用户似乎都是大型企业类型的公司,并且工作负载似乎与工作流相关,而不是实时游戏更新相关的。
有没有人对这些技术或其实现具有白天经验可以分享一下?

16
我想总结一下我们最终做了什么。Rabbit、Qpid、ZeroMQ和其他框架在设计上更注重商业需求,而不是低延迟方面的选择,并且容易出现需要信任客户端或无法支持高频繁加入/离开/队列创建/删除等问题。XMPP在第一个物理盒子之间的联邦效果不好。JMS甚至比Rabbit和其他框架更糟糕。Redis Pub/Sub很有意思,但同样不支持联邦/集群。我们最终使用Erlang/OTP(与Rabbit和ejabberd使用相同的语言)编写了自己的框架,用Google协议缓冲作为低级别的接口定义语言。 - Jon Watte
谢谢分享,"XMPP在第一个物理盒子上无法很好地联合"是什么意思? - alex
3
我的意思是,“在第一个物理盒子之后无法很好地联邦”。添加硬件对于扩展并没有太大作用,因为XMPP协议在这里是错误的选择。 - Jon Watte
5个回答

13

@MSalters

关于"消息队列":

RabbitMQ的默认操作与您描述的完全相同:瞬态发布-订阅。但使用的是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将其降至低于300微秒。TCP窗口管理原始UDP更昂贵,但如果使用UDP以加快速度,并添加自定义丢失恢复或序列号/确认/重传管理器,则可能会再次减慢速度。这完全取决于您的用例。如果您确实真的需要使用UDP和懒惰的确认等功能,则可以剥离RabbitMQ的TCP并且可能能够实现该功能。

希望这有助于澄清我为什么建议Jon使用RabbitMQ。


感谢您的建议。Rabbit的替代方案是Qpid,它声称在Red Hat低延迟内核上运行时,在单个8核服务器框上每秒处理6百万条消息(!)。但是,我怀疑该服务器同时连接了1万个用户。如果您有比较Rabbit和Qpid的好链接,我很想看看! - Jon Watte
Jon,请问你能告诉我6M的参考资料在哪里吗?我有一种感觉它是指RabbitMQ和Qpid一段时间以前都测试过的(金融市场数据)OPRA feed案例。这是一个很好的案例,但我记得我们都使用了批处理和压缩来获得更高的速率。请注意,在OPRA的情况下,同时使用批处理和压缩是标准做法。最近比较两个经纪人在类似的情况下,没有什么立即想到的,但通过谷歌搜索可能会发现更多信息。谢谢Alexis - alexis
是的,那可能是测试案例。6M数字出现在Red Hat网站上,用于他们基于“低延迟Linux”的Qpid实现。 而且,那个测试案例几乎与我感兴趣的案例无关,后者存在一个问题,即有100万个连接用户,每个用户每秒只能收到几条消息... - Jon Watte

11

我正在建立这样的系统。

我对多个消息队列进行了充分评估,包括RabbitMQ、Qpid和ZeroMQ。任何一个消息队列的延迟和吞吐量对于这种类型的应用程序来说都足够了。然而,当半百万个或更多的队列被创建时,队列创建时间是不好的。尤其是Qpid在几千个队列后会严重降级。为了解决这个问题,通常需要创建自己的路由机制(总队列数较少,并且消费者从这些队列获取他们不感兴趣的消息)。

我的当前系统可能会在集群中以一种相当有限的方式使用ZeroMQ。客户端的连接则使用我构建的基于libev的自定义sim.守护进程处理,完全单线程(并且显示出非常良好的扩展性——它应该能够轻松处理一个盒子上的50,000个连接——我们的sim.滴答声率相当低,没有物理作用)。

XML(因此XMPP)非常不适合这种情况,因为您将在处理XML之前让CPU达到极限,而I/O并没有达到瓶颈,这不是您想要的。目前我们使用Google Protocol Buffers,它们似乎非常适合我们的特定需求。我们还使用TCP进行客户端连接。我之前有过使用UDP和TCP的经验,正如其他人指出的那样,UDP确实有一些优点,但它稍微难以使用。

希望在我们离推出更近时,我能够分享更多细节。


5
Jon,这似乎是AMQP和RabbitMQ的理想用例。我不确定为什么您会说AMQP用户都是大型企业类型的公司。我们超过一半的客户都在“网络”领域,从巨型企业到小型公司不等。许多游戏、博彩系统、聊天系统、类推特系统和云计算基础设施都是由RabbitMQ构建的,甚至有移动应用程序。工作流只是许多用例中的一个。
我们试图跟踪这里发生的事情:http://www.rabbitmq.com/how.html (确保你点击进入del.icio.us上的用例列表!)
请务必查看。我们在这里提供帮助。随时发送电子邮件到info@rabbitmq.com或在Twitter上联系我 (@monadic)。

3

我的经验是使用一个非开放式的选择,BizTalk。我们学到的最痛苦的教训是,这些复杂的系统快。并且从硬件要求中你也可以看出,这直接转化为显著的成本。

因此,在核心接口中不要使用XML。你的服务器集群将会解析每秒2百万个消息。这可能轻易地达到每秒2-20 GB/sec的XML!然而,大多数消息将会针对一些队列,而大多数队列实际上都是低流量的。

因此,设计你的架构使其易于从COTS队列服务器开始,然后在识别出瓶颈时将每个队列(类型)移动到自定义队列服务器。

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


2
是的:TCP 的问题在于当你丢失一个数据包时。恢复之前的停顿可能是显著的——而且使用 TCP,内核将会保留信息,只因为信息还没有到达。对于游戏(例如位置更新),这是不可取的。请注意,消息队列客户端都是分布在全球的用户——它们不在集群内部,因此网络问题是生活中的事实。(实际上,即使在连接良好的服务器房间内,您也会看到一定量的数据包丢失,似乎无论交换机和缓冲区有多大) - Jon Watte

3

FWIW,对于中间结果不重要的情况(例如位置信息),Qpid有一个“最新值队列”,可以向订阅者仅传递最新的值。


太好了,知道这个消息真是太好了!我已经检查并构建了Qpid,并正在尝试它。我不太喜欢的是默认配置限制为300个连接。它能在一个盒子上处理20,000个连接吗? - Jon Watte
我在users@qpid.apache.org列表中做了更深入的回答,但为了在这里缩小差距,是的,假设您拥有足够的硬件性能,每个盒子执行20,000个连接应该不成问题。 - Steve Huston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接