18

想想 MUD/MUCK,但可能带有头像或语言环境插图。我选择的语言是ruby​​ 。

我需要处理多个持久连接,数据在服务器及其各个客户端之间异步传输。单个数据库必须根据客户端会话中发生的活动保持最新。每个客户端会话中的活动可能需要立即更新多个其他客户端(用户进入房间;用户向另一个用户发送私人消息)。

这是一个目标项目 ,也是一个学习项目,所以我的意图是重新发明一两个轮子来学习更多关于并发网络编程的知识。但是,我对并发编程和网络编程都很陌生。以前,我几乎只在 Web 应用程序中的非持久、同步 HTTP 请求领域工作。所以,我想确保我正在重新发明正确的轮子。

根据emboss的出色回答,我已经开始研究某些 HTTP 服务器的内部结构,因为 Web 应用程序通常可以避免线程问题,因为服务器本身将问题抽象得如此彻底。

我不想使用 EventMachine 或 GS​​erver,因为我还不了解它们的作用。一旦我大致了解了它们的工作原理、它们解决了哪些问题以及它们为何有用,我就会对它感到满意。我的目标不是“编写游戏”,而是“编写游戏并了解一些较低级别的东西是如何工作的”。我也不清楚某些术语的界限;例如,“I/O-unbound apps”是“event-driven apps”的超集吗?反之亦然?

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

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

我现在做事的方式是使用IO#select阻塞连接的套接字列表,超时为0.1秒。它将读取的任何信息推送到线程安全的读取队列中,然后每当遇到超时时,它就会从线程安全的写入队列中提取数据。我不确定超时是否应该更短。还有第二个线程轮询套接字处理线程的读取队列并处理“请求”。这比我最初的工作方式要好,但仍然可能不理想。

我在 Hacker News 上发布了这个问题,并链接到了我正在研究的一些资源;任何类似的东西都会很棒:

4

4 回答 4

13

尽管您可能不喜欢听到它,但我仍然建议您先开始研究 HTTP 服务器。尽管为他们编写程序对您来说似乎很无聊、同步且不持久,但这只是因为服务器的创建者做了他们的工作,非常好地向您隐藏了血淋淋的细节——如果您考虑一下,Web 服务器就不是这样了同步的(并不是说数百万人必须等待阅读这篇文章直到你完成......并发:)......因为这些野兽做得很好(是的,我知道我们经常对他们大喊大叫,但是归根结底,大多数 HTTP 服务器都是出色的软件)如果您想了解高效的多线程,这是明确的起点。操作系统和编程语言或游戏的实现是另一个很好的来源,但可能离你想要实现的目标有点远。

如果你真的想弄脏你的手指,我建议你先把自己定位在WEBrick之类的东西上——它随 Ruby 一起提供,并且完全用 Ruby 实现,所以你将在那里学习所有关于 Ruby 线程概念的知识。但请注意,您将永远无法接近 Rack 解决方案的性能,该解决方案位于以 C 语言实现的 Web 服务器之上,例如Thin

因此,如果您真的想认真,您将不得不在 C(++) 中推出自己的服务器实现,并且如果您打算支持 HTTP,则可能使其支持 Rack。我会说这是一项相当艰巨的任务,尤其是如果您希望最终结果具有竞争力。C 代码可以非常快,但也很容易变得非常慢,这是低级代码的本质。而且我们还没有讨论内存管理和安全性。但如果这真的是你的愿望,那就去做吧,但我会首先深入研究知名的服务器实现以获得灵感。了解它们如何使用线程(池)以及它们如何实现“会话”(您想要持久性)。您想要的所有事情都可以使用 HTTP 完成,如果使用巧妙的 REST 接口,效果会更好,支持您提到的所有功能的现有应用程序就是证明。所以朝那个方向走也不是完全错误的。

如果您仍想发明自己的专有协议,请将 TCP/IP 作为可接受的最低公分母。超越这一点最终会导致您的孙子们可能仍在编码的项目中。在网络编程方面,这真的是我敢于做到的最低水平。

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

根据您的要求:异步通信,多个“订阅者”对集中发布的“事件”做出反应;好吧,这听起来确实是事件驱动/基于消息的架构的不错选择。


一些可能对您的旅程有所帮助的书籍(仅限 Linux/C,但概念是通用的):

(那些都是经典)


您可能想要查看的项目:

于 2011-07-27T22:18:49.243 回答
3

我建议阅读一些有关独角兽网络服务器设计的内容。这应该让您对线程与进程的讨论有所了解。

http://unicorn.bogomips.org/DESIGN.html

http://tomayko.com/writings/unicorn-is-unix

于 2011-07-27T21:48:35.230 回答
2

我对 Ruby 了解不多 - 抱歉 - 但我认为架构需要由您的需求驱动(母性,苹果派......我知道)。

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

响应时间也很重要——我认为这对于 MUD 风格的游戏来说没什么大不了的,但对于网络服务器或 FPS 游戏来说,这是一个大问题。

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

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

于 2011-08-11T13:35:02.520 回答
-1

正确的方法是使用测试驱动开发。然后,您将在需要发明的确切时刻发现需要重新发明的东西。

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

于 2011-07-27T21:46:55.803 回答