我扩展nodejs应用程序的最佳方式是什么



基础

现在,我和一些朋友正在尝试开发一款用nodejs制作的浏览器游戏。这是一个多人自上而下的射击游戏,客户端和服务器端的大部分代码都是javascript。我们有一个很好的总体方向,我们想进入,我们有很多乐趣开发游戏。我们制作这场比赛的目标之一是尽可能地让作弊变得困难。这样做,我们就可以在服务器端处理所有的游戏逻辑。客户端只通过网络套接字向服务器发送他们的输入,服务器用游戏中发生的事情更新客户端(也是网络套接字)。这是我们问题的开始。

所有服务器端的计算都变得相当繁重,我们发现我们需要以某种方式进行扩展,以处理超过10名玩家的游戏(我们希望能够容纳更多玩家)。起初,我们认为可以根据需要进行垂直扩展,但由于nodejs是单线程的,因此只能利用一个核心。这意味着获得一个更强大的服务器并不能解决这个问题。我们唯一的解决方案是横向扩展。

我们为什么在这里问

我们还没有找到任何关于如何扩展nodejs游戏的好例子。我们的用例非常特殊,虽然我们已经尽了最大努力自己完成这项工作,但我们确实可以从外部意见和建议中受益

详细信息

我们已经为如何解决这个问题花了很多心思。我们已经做了一个多星期了。以下是我们迄今为止所做的:

四种类型的服务器

我们将任务划分为4种不同类型的服务器。每个人都将完成一项特定的任务。

代理服务器

代理服务器将位于整个堆栈的前面,是唯一可以从互联网直接访问的服务器(可能会有更多这样的服务器)。它上面会有haproxy,它会将所有连接路由到web服务器。我们之所以选择haproxy,是因为它拥有丰富的功能集、可靠性和几乎无与伦比的速度。

web服务器

网络服务器将接收网络请求,并为所有网页提供服务。他们还将处理大厅创建/管理和游戏创建/管理。为了做到这一点,他们会告诉游戏服务器它有什么大厅,大厅里有什么用户,以及他们将要玩的游戏的信息。然后,网络服务器将更新游戏服务器的用户输入,游戏服务器将更新网络服务器(然后更新客户端)的游戏中发生的事情。网络服务器将使用TCP套接字与游戏服务器就任何类型的管理进行通信,并且在就游戏更新进行通信时将使用UDP套接字。这一切都可以用nodejs完成。

游戏服务器

游戏服务器将处理有关游戏的所有游戏数学和变量更新。游戏服务器还和数据库服务器通信,记录游戏中玩家的酷数据。这将使用nodejs来完成。

数据库服务器

数据库服务器将承载数据库。事实证明,这部分是最简单的,因为我们发现了rethinkdb,有史以来最酷的db。这很容易扩展,而且奇怪的是,它是扩展我们的应用程序最简单的部分。

其他一些细节

如果你很难理解我们的整个打扮,看看这个,这是一个我们认为我们将如何缩放的半准确图表。

如果你只是好奇,或者认为看看我们的游戏可能会有所帮助,它目前在这里处于未缩放状态。

有些东西我们不想要

  • 我们不想使用nodejs的集群模块。它不稳定(这里说),而且它不能扩展到其他服务器,只能扩展到其他处理器。我们只想跳到水平缩放

我们的问题总结如下

我们希望我们正朝着正确的方向前进,我们已经做了功课,但我们不确定。我们当然可以就如何以正确的方式做到这一点提出一些建议。

谢谢

我意识到这是一个很长的问题,做出一个深思熟虑的答案并不容易,但我真的很感激

谢谢!!

以下是我对您的案例的自发想法:

多核使用

node.js也可以使用多个内核进行扩展。如何,你可以阅读这里的例子(或思考一下:你有一个线程/进程在一个核心上运行,你需要使用多个核心吗?多个线程或多个进程。将工作从主线程推送到其他线程或进程,你就完成了)。

我个人认为,开发一个不使用多个核心的应用程序是幼稚的。如果你使用了一些后台进程,好吧,但如果你到目前为止只在node.js主事件循环中工作,你肯定应该投入一些时间来使应用程序在核心上可扩展。

顺便说一句,实现IPC这样的东西并不是那么容易。你可以这样做,但如果你的情况很复杂,也许你可以使用集群模块。这显然不是你的最爱,但仅仅因为一些东西被称为"实验性的"并不意味着它是垃圾。只要尝试一下,也许你甚至可以在途中修复模块的一些错误。对于复杂的问题,使用一些广泛使用的软件可能比发明一个新的轮子要好。

您还应该(如果您还没有)考虑(明智地)使用nextTick功能。这允许主事件循环暂停一些cpu密集型任务,同时执行其他工作。例如,你可以在这里阅读。

关于计算的一般思想

你绝对应该仔细研究一下你的游戏引擎算法。你已经注意到这是你现在的瓶颈,实际上计算是大多数游戏中最关键的部分。缩放确实以一种方式解决了这个问题,但缩放会带来其他问题。此外,你不能把"缩放"作为解决问题的方法,而期望每个问题都消失。

你最好的选择是让你的游戏代码优雅而快速。思考如何有效地解决问题。如果你不能有效地用Javascript解决问题,但问题可以很容易地提取出来,为什么不写一个C组件呢?这也算是一个单独的过程,可以减少主节点.js事件循环的负载。

代理

就我个人而言,我现在看不出代理级别的优势。你似乎不期望有大量的用户,因此你不需要解决CDN解决的问题或其他什么。。。考虑一下没关系,但我现在不会在那里投入太多时间。

从技术上讲,你的网络服务器软件很有可能提供代理功能。所以把它写在纸上是可以的,但我现在不会计划使用专用硬件。

结语

其余的对我来说似乎或多或少都很好。

游戏有点晚,但请看这里:http://goldfirestudios.com/blog/136/Horizontally-Scaling-Node.js-and-WebSockets-with-Redis

你没有提到任何与内存管理有关的事情。正如您所知,nodejs不与其他进程共享内存,因此如果您想扩展,必须使用内存中数据库。(RedisMemcache等)。你需要设置一个发布者&subscriber事件,以接受来自redis的传入请求。通过这种方式,您可以扩展x nilo数量的服务器(在您的HAProxy前面),并利用从redis传输的数据。

还有这个node插件:http://blog.varunajayasiri.com/shared-memory-with-nodejs这允许您在进程之间共享内存,但仅适用于Linux。如果您不想一直在本地进程之间发送数据,或者不想处理nodesipc-api,这将有所帮助。

您还可以在node中为新的v8隔离派生子进程,以帮助处理昂贵的cpu绑定任务。例如,玩家可以在我的动作rpg游戏中杀死怪物并获得相当多的战利品。我有一个名为LootGenerater的子进程,基本上,每当玩家杀死怪物时,它都会通过默认的IPC api.send向该进程发送游戏idmob_iduser_id。一旦子进程收到它,它就会遍历大型loot表并管理项目(存储到redis或其他什么),然后将其管道返回。

这有助于极大地解放事件循环,这是我能想到的帮助您扩展的一个想法。但最重要的是,你会想使用内存中的数据库系统,并确保你的游戏代码架构是围绕你使用的任何数据库系统设计的。不要犯我现在不得不重写所有内容的错误:)

希望这能有所帮助!

注意:如果您决定使用Memcache,则需要使用另一个pub/sub系统

最新更新