我可以扩展nodejs应用程序的最佳方法是什么?

时间:2014-02-24 07:14:53

标签: javascript node.js sockets scalability autoscaling

基础知识

现在我的一些朋友和我正在尝试开发一个在nodejs中制作的浏览器游戏。这是一个多人自上而下的射击游戏,客户端和服务器端代码中的大多数都是javascript。我们有一个很好的总体方向,我们想进去,我们在开发游戏方面有很多乐趣。制作这款游戏​​时我们的目标之一就是让它尽可能地作弊。做到这一点,我们所有的游戏逻辑都在服务器端处理。客户端仅通过Web套接字将其输入发送到服务器,并且服务器使用游戏中发生的事情更新客户端(也是Web套接字)。这是我们问题的开始。

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

为什么我们在这里问

我们未能找到如何扩展nodejs游戏的任何好例子。我们的用例非常特别,虽然我们已尽力做到这一点,但我们真的可以从外部意见和建议中受益

详细

我们已经对如何解决这个问题投入了大量的思考。我们已经研究了一个多星期了。以下是我们到目前为止所做的工作:

四种类型的服务器

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

代理服务器

代理服务器将位于整个堆栈的前端,并且是唯一可以从Internet直接访问的服务器(可能有更多这些服务器)。它会有haproxy,它会将所有连接路由到Web服务器。我们之所以选择haproxy,是因为它具有丰富的功能集,可靠性和几乎无与伦比的速度。

网络服务器

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

游戏服务器

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

数据库服务器

db服务器将托管数据库。这部分实际上是最简单的,因为我们找到了rethinkdb,这是有史以来最酷的数据库。这很容易扩展,奇怪的是,它是扩展我们应用程序最容易的部分。

其他一些细节

如果你无法理解我们的整个启动过程,look at this,这是一个半精确的图表,表明我们认为我们将如何扩展。

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

我们不想要的一些事情

  • 我们不想使用nodejs的集群模块。它不稳定(表示here),并且它不能扩展到其他服务器,只能扩展到其他处理器。我们想要实现横向扩展。

我们的问题,总结

我们希望我们朝着正确的方向前进,我们已完成了我们的功课,但我们不确定。我们当然可以采取一些有关如何以正确方式执行此操作的提示。

感谢

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

谢谢!

2 个答案:

答案 0 :(得分:12)

跟随我对你的案子的自发想法:

多核用法

node.js 也可以扩展到多个核心。怎么样,你可以阅读for example here或者只是想一想:你在一个核心上运行一个线程/进程,你需要使用多个核心?多线程或多个进程。推动主要工作线程到其他线程或进程,你就完成了)。

我个人会说开发一个不使用多核的应用程序是幼稚的。如果您使用了一些后台进程,那么,如果您现在只在 node.js 主事件循环中工作,那么您应该花一些时间让应用程序在核心上进行扩展。 / p>

顺便说一句,实现类似IPC的东西并不容易。你可以这样做,但是如果你的情况很复杂,那么你可以选择集群模块。这显然不是你最喜欢的,但仅仅因为某些东西被称为“实验性”,它并不意味着它是无用的。试一试,也许你甚至可以修复模块中的一些错误。最有可能使用一些广泛使用的软件来解决复杂的问题,而不是发明一个新的轮子。

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

关于计算的一般想法

你一定要仔细看看你的游戏引擎算法。您已经注意到这是您现在的瓶颈,实际上计算是大多数游戏中最重要的部分。缩放确实以一种方式解决了这个问题,但缩放引入了其他问题。此外,你不能把“缩放”作为问题解决者抛在一切上,并期望每个问题都消失。

您最好的选择是让您的游戏代码优雅而快速。想想如何有效地解决问题。如果你无法有效地解决Javascript中的问题,但问题很容易被提取,为什么不写一点C组件呢?这也算作一个单独的进程,这减少了主 node.js 事件循环的负载。

代理?

就我个人而言,我现在还没有看到代理级别的优势。您似乎不期望大量用户,因此您不需要解决CDN解决等问题......可以考虑一下,但我现在不会在那里投入太多时间。

从技术上讲,您的网络服务器软件很有可能提供代理功能。因此可以将它放在纸上,但我现在不打算使用专用硬件。

后记

其余对我来说似乎或多或少。

答案 1 :(得分:1)

比赛迟到,但请看一下:http://goldfirestudios.com/blog/136/Horizontally-Scaling-Node.js-and-WebSockets-with-Redis

您没有提及与内存管理有关的任何内容。如您所知,nodejs不与其他进程共享其内存,因此如果要扩展,则必须使用内存数据库。 (RedisMemcache等)。您需要设置发布商&每个节点上的订阅者事件,以接受来自redis的传入请求。这样,您可以扩展x nilo数量的服务器(在您的HAProxy前面)并利用从redis传输的数据。

还有这个node插件:http://blog.varunajayasiri.com/shared-memory-with-nodejs这可以让您在进程之间共享内存,但只能在Linux下运行。如果您不想一直在本地进程中发送数据或者必须处理nodes ipc api,这将有所帮助。

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

这有助于大大释放事件循环,只有一个我能想到的想法可以帮助你扩展。但最重要的是,您将需要使用内存数据库系统,并确保您的游戏代码体系结构是围绕您使用的任何数据库系统设计的。不要犯错我现在不得不重写一切:)

希望这有帮助!

注意:如果您决定使用Memcache,则需要使用其他发布/订阅系统。