node.js集群中的subprocess间通信选项

所以我现在在做一个node.js游戏服务器应用程序,在这里我碰到了一些问题。 我的问题是,我使用socket.io接受来自游戏客户端的入站连接。 这些客户端可能连接到游戏世界的几个区域或区域之一。

基本架构如下所示。 主进程为运行区域pipe理器进程的每个游戏区域分配一个subprocess; 一个专门维护区域数据的过程(三维模型,玩家/实体的位置等)。 主进程然后为其创build的每个区域pipe理器分派多个“通信线程”。 这些线程创build一个socket.io的实例并侦听该区域的端口(多个线程在单个端口上侦听)。 这些线程将在自己的进程中处理大部分游戏逻辑,并与支持游戏服务器的数据库进行通信。 唯一的问题是,在某些情况下,他们可能需要与区域经理沟通,以获得关于区域,球员等的信息。

建筑

举个例子:一个玩家想要在该区域内购买/出售/交易非玩家angular色(NPC)。 区域通信线程需要询问区域pipe理器线程,以确定玩家是否足够接近NPC进行交易,然后才允许交易发生。

我在这里遇到的问题是我打算利用node.js集群function,并使用进程的send()on()方法来回传递消息。 除了我遇到的一个警告之外,这将是很好的。 由于cluster.fork()所有subprocess都只能与“主”进程通信。 node.js根进程成为所有通信的瓶颈。 我使用一个脚本在我的系统上运行了一些基准testing,它使用集群的进程间通信(IPC)来回传递消息,并跟踪每秒钟执行多less次中继。 看起来,最终节点以每秒约20k的速度在它可以中继的IPC数量方面达到最大值。 这个数字在Phenom II 1.8ghz四核笔记本电脑和FX-8350 4.0ghz 8核心桌面电脑上都是一致的。

现在这听起来相当高,除了这基本上意味着无论有多less区域或通信线程,所有的IPC仍然是通过一个单一的进程瓶颈,作为整个应用程序的“中继”。 这意味着尽pipe看起来每个单独的线程可以每秒钟接续20k个IPC,但是由于所有的通信都是通过一个线程进行的,整个应用程序作为一个整体将永远不会中继,即使是在一些疯狂的32核心系统上。

所以这就是我遇到的问题。 现在困境。 我已经阅读了很多有关其他各种选项,并阅读了20个不同的问题在这里堆栈关于这个话题,我已经看到了一些事情经常popup:

Redis :我实际上是在我的服务器上运行Redis,并将其用作socket.io数据存储,以便多个线程中的socket.io可以共享连接数据,以便用户可以连接到N个socket.io线程为他们的区域,所以服务器可以自动负载平衡input连接。

我关心的是它贯穿networking堆栈。 对于同一台服务器上的多个进程之间的通信几乎不是理想的 我觉得延迟将是一个长期的主要问题。

0MQ(zeromq / zmq) :我以前从来没有用过这个,但最近我已经听到了一些。 根据我所做的阅读,我发现了很多人使用TCP套接字的例子,但是对于使用IPC的人来说并没有太多的嗡嗡声。 我希望也许有人在这里为IPC工作过0MQ(甚至可能在node.js中),并且可以为我提供一些启示。

dnode :我再也没有用过这个,但是从我所看到的看来,这是另外一个devise用于TCP的选项,这意味着networking堆栈再次成为可能。

node-udpcomm :有人在这里的另一个问题(这是我不能再次不幸find)连接这个。 我从来没有听说过,它看起来像一个非常小的解决scheme,打开和侦听UDP连接。 虽然这可能仍然会比TCP选项更快,但我们仍然有networking堆栈的方式吗? 我肯定喜欢在我的“程序员区”以外的一英里,就像这里,以及我不太了解的networking/计算机体系结构的东西。

无论如何,底线是我完全卡在这里,不知道在这种情况下IPC是最好的select。 我现在假设0MQ是我上面列出的最好的select,因为它是唯一一个似乎为通信协议提供了“IPC”选项,我认为这意味着它使用UNIX套接字或者是不通过networking堆栈,但我不能确认,或任何东西。

我想我只是希望这里的一些人能够足够的了解我的方向,或者告诉我我已经去了那里。 我正在开发的这个项目是一个多人游戏服务器,devise用来与多人游戏客户端“开箱即用”,并以Three.js为3Dgraphics/计算器提供动力。 客户端和服务器将在所有人都满意的情况下成为开放源代码,并且我希望确保架构尽可能地具有可扩展性,这样人们就不会在此基础上构build游戏,然后进行扩展并最终撞墙。

无论如何感谢您的时间,如果你真的读这一切:)

我认为0MQ是一个很好的select,但是我承认我不知道其他人:D

对于0MQ,它是透明的,你决定使用什么传输,库调用是一样的。 这只是在开始调用zmq_bindzmq_connect期间select特定的端点(从而传输)。 基本上有四条路可以决定采取:

  1. "inproc://<id>" – 线程之间通过内存的进程间通信端点
  2. "ipc://<filepath>" – 系统相关的进程间通信端点
  3. "tcp://<ip-address>" – 清除
  4. "pgm://...""epgm://..." – 实用可靠多播的端点

所以简单来说,列表中的数值越高,其速度就越快,考虑到您需要面对的延迟和可靠性问题就越less。 所以你应该尽量保持高度。 由于您的组件是stream程,所以您应该自然地使用IPC传输。 如果您稍后需要更改某些内容,则只需更改端点定义即可。

现在实际上比您select的交通工具更重要的是套接字types,或者您决定使用的模式。 你的情况是一种典型的请求 – 响应types的通信,所以你可以做

  1. 经理:REP套接字,线程:REQ套接字; 要么
  2. 经理:路由器,主题:REQ或DEALER

然后,线程将它们的套接字连接到分配给它们的单个pipe理套接字,就这样,他们可以开始发送请求并等待响应。 他们必须决定的是他们用作端点的path。

但是要详细地描述所有这些套接字types和模式意味着什么,这绝对超出了本文的范围,但是您可以并且应该在ZeroMQ指南中更多地了解它。 在这里,您不仅可以了解所有的套接字types,还可以了解如何连接组件并让它们相互通信的许多不同方法。 我刚才提到的只是一个非常简单的。 一旦你明白,你可以build立任意的层次结构。 这就像乐高;-)

希望它有一点帮助,欢呼!