node.js集群中的子进程间通信选项

时间:2013-01-20 20:53:00

标签: multithreading node.js redis zeromq dnode

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

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

Architecture

作为一个例子:玩家想要在区域中购买/出售/交易非玩家角色(NPC)。区域通信线程需要向区域管理器线程询问玩家是否足够接近NPC进行交易才能进行交易。

我遇到的问题是我计划使用node.js集群功能并使用进程的send()on()方法来处理传递消息来来回回。除了我遇到过的一个警告之外,这没关系。由于与cluster.fork()分离的所有子进程只能与" master"进行通信。处理。 node.js根进程成为所有通信的瓶颈。我使用一个脚本在我的系统上运行了一些基准测试,这个脚本实际上只是使用集群的进程间通信(IPC)来回传递消息,并跟踪每秒执行的继电器数量。似乎最终节点在可以传输多少IPC的情况下以每秒约20k的速度出现。这个数字在Phenom II 1.8ghz四核笔记本电脑和FX-8350 4.0ghz 8核桌面上都是一致的。

现在这听起来相当高,除了这基本上意味着无论有多少个区域或通信线程,所有IPC仍然是通过单个进程的瓶颈,充当"中继"对于整个应用程序。这意味着虽然看起来每个单独的线程都可以继续>每秒20k的IPC,整个应用程序作为一个整体将永远不会传递更多,即使它是在一些疯狂的32核心系统,因为所有通信都通过一个线程。

这就是我遇到的问题。现在进退两难。我已经阅读了很多关于其他各种选项的内容,并且就这个主题在堆栈上阅读了20个不同的问题,并且我已经看到有些事情经常出现:

Redis 我实际上在我的服务器上运行Redis并将其用作socket.io数据存储区,以便多个线程中的socket.io可以共享连接数据,以便用户可以连接到任意N个socket.io其区域的线程,以便服务器可以自动对传入连接进行负载平衡。

我对此的关注是它贯穿网络堆栈。非常适合在同一服务器上的多个进程之间进行通信。从长远来看,我觉得延迟会成为一个主要问题。

0MQ (zeromq/zmq) 我之前从未使用过这个,但最近我听到了一些关于它的内容。根据我已经完成的阅读,我发现了很多人使用TCP套接字的例子,但是人们使用它进行IPC的时候并没有太多的嗡嗡声。我希望也许有人在这之前曾经使用0MQ for IPC(甚至可能在node.js中?)并且可以为我提供这个选项。

dnode 我再次从未使用过这个,但从我所看到的情况来看,它似乎是另一个旨在通过TCP工作的选项,这意味着网络堆栈再次受到阻碍。

node-udpcomm 有人在这里将另一个问题联系起来(不幸的是,我似乎无法再找到这个问题)。我甚至从来没有听说过它,它看起来像一个非常小的解决方案,可以打开并监听UDP连接。虽然这可能仍然比TCP选项更快,但我们仍然拥有正确的网络堆栈?我非常喜欢在我的"程序员专区之外一英里#34;就像在这里,并进入网络/计算机架构的领域,我不太了解lol

无论如何,底线是我完全陷入困境,并且不知道在这种情况下IPC的最佳选择是什么。我现在假设0MQ是我上面列出的那个中最好的选择,因为它是唯一一个似乎提供" IPC"通信协议的选项,我认为它意味着它使用的是UNIX套接字或者没有通过网络堆栈的东西,但是我无法确认这一点或任何东西。

我想我只是希望这里的一些人可能知道足以指出我正确的方向或者告诉我,我已经到了那里。我正在开发的项目是一款多人游戏服务器,设计用于开箱即用#34;多人游戏客户端都使用Three.js为3D图形/计算提供动力。一旦我让所有人都满意地工作,客户端和服务器将成为开放源代码,我想确保架构尽可能地扩展,这样人们就不会在此基础上构建游戏然后去扩大规模并最终击中墙壁。

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

1 个答案:

答案 0 :(得分:8)

我认为0MQ将是一个非常好的选择,但我承认我不了解其他人:D

对于0MQ,您决定使用的传输是透明的,库调用是相同的。这只是在开始时调用zmq_bindzmq_connect期间选择特定端点(以及传输)。您可以决定采用以下四种途径:

  1. "inproc://<id>" - 线程之间通过内存进行的进程间通信
  2. "ipc://<filepath>" - 依赖于系统的进程间通信端点
  3. "tcp://<ip-address>" - 清除
  4. "pgm://...""epgm://..." - Pragmatic Reliable Multicast的端点
  5. 所以简单来说,列表越高,它就越快,考虑到你必须面对的延迟和可靠性的问题就越少。所以你应该尽量保持高度。由于您的组件是进程,因此您应该自然地使用IPC传输。如果您以后需要更改某些内容,则只需更改端点定义即可。

    现在实际上比您选择的传输更重要的是套接字类型,或者您决定使用的模式。您的情况是典型的请求 - 响应类型的通信,因此您可以执行

    1. Manager:REP socket,Threads:REQ socket;或
    2. 经理:ROUTER,主题:REQ或经销商
    3. 然后,线程将其套接字连接到分配给它们的单个Manager套接字,这就是全部,它们可以开始发送请求并等待响应。他们必须决定的是他们用作终点的路径。

      但是要详细描述所有这些套接字类型和模式的含义绝对超出了本文的范围,但您可以而且应该在ZeroMQ Guide中阅读更多相关内容。在那里,您不仅可以了解所有套接字类型,还可以了解如何连接组件并让它们相互通信的许多不同方法。我提到的那个只是一个非常简单的。理解后,您可以构建任意层次结构。这就像乐高; - )

      希望它有所帮助,欢呼!