我应该如何设计我的(主要是)基于文本的游戏服务器架构?

19

希望创建一款类似MUDs/MUCKs的游戏,但需要有头像或地点插图。我选择使用ruby语言。

我需要处理多个持久连接,实现服务器和客户端之间异步传输数据。必须保持单个数据库基于客户端会话中发生的活动而更新。每个客户端会话中的活动可能需要立即更新其他多个客户端(例如:一个用户进入房间;一个用户给另一个用户发送私人消息)。

这是一个目标项目学习项目,因此我的意图是重复发明某些轮子,以了解更多关于并发网络编程的知识。然而,我对并发和网络编程都很陌生;以前我几乎完全在非持久、同步的HTTP请求的Web应用程序中工作。因此,我想确保我正在重复发明正确的轮子。

根据emboss的出色回答,我开始研究某些HTTP服务器的内部实现,因为Web应用程序通常可以避免线程问题,因为这些问题被服务器本身彻底抽象化了。

我不想使用EventMachine或GServer,因为我还不理解它们做什么。一旦我对它们的工作原理有一个基本的概念,知道它们解决了哪些问题以及它们为什么有用,我就会感到舒适。我的目标不是"写一个游戏",而是"写一个游戏并了解一些更底层的东西"。我也不清楚某些术语的边界;例如,"I/O未绑定的应用程序"是否是"事件驱动应用程序"的超集?反之亦然?

当然,如果存在一种正确的方法来实现我的目标,我当然很感兴趣,但总体上我想了解为什么它是正确的方式,为什么其他方法不太可取。

任何书籍、电子书、在线资源、示例项目或其他建议都是我真正想要的

我目前的做法是使用IO#select阻塞在已连接套接字列表上,超时时间为0.1秒。它将读取到的任何信息推送到线程安全的读取队列中,每当达到超时时间时,就从线程安全的写入队列中获取数据。我不确定超时时间是否应该更短。还有第二个线程轮询套接字处理线程的读取队列并处理"请求"。这比我最初的处理方式要好,但可能仍不理想。
我在Hacker News上发布了这个问题,并得到了一些链接资源,我正在学习;类似的东西很好:

2
在软件开发中,几乎从来没有“唯一正确的方法”,在像这样的游戏中,数据库通常是主要的限制因素。应用程序代码的性能很可能远远超过数据库的性能,您需要考虑缓存解决方案或数据库分片解决方案。对于游戏来说,分片可能非常合适,因为您可以按游戏区域进行分片。 - Chris Marisic
我认为游戏中的资源将会很少,以至于我可以在保持一个"更新队列"的同时将整个世界存储在内存中,并将每次更改写入数据库的操作放到单独的线程中。这样,游戏本身就不会被绑定在数据库操作上,唯一的“读取”操作将只发生在启动时。虽然处理错误的风险很高,但如果我能使它正常工作,那将非常方便。 - Max Cantor
1
这对于小规模解决方案来说是可行的,但我认为您可能低估了游戏在数据库使用方面的需求。您需要跟踪库存、玩家和怪物的状态/健康/位置等信息。库存数据库延迟是困扰MMO开发最常见的问题之一。 - Chris Marisic
4个回答

13

虽然你可能不愿意听,但我仍然建议先开始研究HTTP服务器。尽管对它们进行编程似乎很无聊、同步和非持久性,但这只是因为服务器的创建者非常出色地隐藏了这些麻烦的细节--如果你想一想,一个Web服务器是如此同步(数百万人不必等待你完成阅读本帖子...并发 :)..并且由于这些巨兽工作得如此出色(是的,我知道我们经常批评它们,但在一天结束时,大多数HTTP服务器都是优秀的软件),这是学习高效多线程的绝对起点。操作系统、编程语言或游戏实现是另一个好的信息来源,但可能离你所想要实现的目标略远。

如果你真的打算深入研究,我建议你首先看看类似WEBrick 这样的东西 - 它与Ruby一起发行,并且完全使用Ruby实现,因此你将在那里学习有关Ruby线程概念的所有内容。但请注意,你永远无法接近基于C实现的Web服务器上运行的Rack解决方案,例如thin的性能。

如果您真的想要认真对待这个问题,您可能需要自己用C(++)编写服务器实现,并且很可能需要支持Rack,如果您打算支持HTTP。我会说这是一项相当艰巨的任务,特别是如果您希望最终结果具有竞争力。C代码可能非常快,但速度也很容易变得非常慢,这在低级别的东西中是常见问题。我们还没有讨论内存管理和安全性。但如果这确实是您的愿望,请去做吧,但我建议先深入了解众所周知的服务器实现,以获取灵感。看看它们如何处理线程(池化)以及如何实现“会话”(您想要的持久性)。所有您想要的东西都可以使用HTTP完成,尤其是与聪明的REST接口一起使用时,支持您提到的所有功能的现有应用程序就是明显的证明。因此,朝着那个方向走并不完全错误。

如果您仍然想发明自己的专有协议,请将其基于TCP/IP作为最低可接受的共同点。超越这个范畴将导致一个项目,您的孙子可能仍在编码中。当涉及网络编程时,这是我敢于去的最低层次。

无论您是否将其用作库,请查看EventMachine及其概念模型。在了解/重新发明“正确”的轮子的情况下,忽视事件驱动(“非阻塞”)IO将是疏忽的。了解事件驱动编程的胃口,解释了node.js作为Web服务器的好处。

基于您的要求:异步通信,多个“订阅者”对“事件”做出反应,这些事件由中央发布;那么这真的听起来像是一个很好的候选事件驱动/消息传递架构。


以下是一些可能有助于您旅程的书籍(仅限Linux/C,但概念是普遍的):

(这些都是经典书籍)

  • Linux 编程接口 - 如果你只想买一本书的话,选择这本就好了。我还没完全看完,但它真的很棒,可以涵盖你在探索过程中所需要的所有主题。

你可能想要查看的项目:

  • Apache 2、thin、mongrel、nginx、lighttpd 等各种 web 服务器
  • EventMachine(抱歉 :)
  • node.js
  • 在游戏中进行高效的网络通信(Quake 3源代码

  • 感谢您的详细回复!我表达得不够清楚,我的意思并不是我不想理解EventMachine的工作原理。我的意思是,我不希望别人说“只需使用EventMachine”。所以,无需道歉!这不是您所说的,我很感激您的建议 :-) - Max Cantor
    顺便说一下,我在思考你写的内容,我不认为HTTP是最好的工具。我喜欢你解释为什么HTTP服务器是一个很好的学习资源,我也同意,但是需要从服务器实时更新推送到客户端的需求使得HTTP成为游戏服务器任务的不良选择。这需要像彗星这样的巧妙技巧才能实现TCP套接字免费提供的功能。我错了吗? - Max Cantor
    1
    是的,你说得非常正确。当从服务器接收事件时,HTTP表现很差。大多数情况下,这是通过持续轮询(显然不理想)或像Comet这样真正复杂的东西来处理的,因此HTTP实际上更多地在拉取而不是推送方面发挥作用。我只是在想到基于Web的游戏时读/写了这个,所以我认为你必须无论如何使用HTTP(也许我误解了)。但如果这不是必需的,那么您可以自由选择协议,我几乎可以肯定,在这种情况下有比HTTP更适合的某些(基于TCP的)东西。 - emboss
    1
    喜欢TCPServer吗?如果您正在寻找使用Telnet Negotiation的TCPServer资源和架构方法,我可以为您提供指导。 - stslavik
    是的,那将是相当棒的! - Max Cantor

    3

    2

    对于Ruby,我不是很了解 - 抱歉 - 但我认为架构需要根据您的要求进行驱动(母性、苹果派...我知道)。

    如果您正在构建需要扩展到大量用户的东西,则您的架构需要反映出这一点 - 并且您最终会做出各种决策,这些决策在更适度的规模下不一定需要做出。

    响应时间也起着重要作用 - 我认为在MUD风格的游戏中这并不是一个大问题,但对于Web服务器或FPS游戏来说,这是一个巨大的问题。

    话虽如此 - 我所知道的与您描述的类似的系统都使用事件驱动的编程模型 - 客户端触发事件,系统更新其内部状态,并通知受影响的客户端。 "内部状态" 实际上存储在单独的应用程序中,再次使用套接字进行通信 - 这允许通过添加更多服务器来处理客户端交互来扩展应用程序。不确定您是否需要那种复杂程度。

    线程确实是一个真正的问题,它会创建难以测试的代码,因此尽管dash-tom-bang的答案确实有点离题,但可测试性确实是一个重要的问题。


    -1

    正确的方法是使用测试驱动开发。这样,您将在需要发明时精确地发现需要重新发明的内容。

    从一个“连接”测试开始,并断言返回消息“你好,用户”。从那里一步一步进行。


    5
    我给你的回答点了踩,因为我问的是关于架构的问题,而你的回答涉及编程方法论,这使它与问题无关。我实际上非常喜欢测试驱动开发,并且一直在这个项目中使用它;但它的实用性与高级服务器架构的问题无关。 - Max Cantor
    是的,测试驱动开发(与简单的单元测试相反)可以指导您的架构,但这是一个常见的误解。不过没关系,没有恶意;我们不能都有正确的答案。 :) 无论如何,我的观点是,没有比定义需求然后根据这些需求实现更好的学习方式了。 - dash-tom-bang
    3
    我忍不住要问一句...你真的在暗示说,只要严格遵循TDD原则,我就能够编写出高性能、并发的代码,并且完全没有竞态条件、内存泄漏或者任何意外行为,而且完全不需要参考任何其他关于这个问题领域的资源吗? - Max Cantor
    2
    抱歉,我的理解是你想要“重新发明一些轮子”,而不是针对处理任何可能负载的世界级史诗服务器。当我想学习新东西时,就是这样做的。如果你想要创建最好的产品,那么显然需要看看别人做过什么并在此基础上构建。 - dash-tom-bang

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