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

基础

现在我的一些朋友正在尝试开发一个用nodejs制作的浏览器游戏。 这是一个多人自上而下的射手,大部分的客户端和服务器端的代码是在JavaScript中。 我们有一个很好的总体方向,我们希望进入,我们正在开发游戏很有趣。 我们做这个游戏的目标之一就是尽可能的努力去作弊。 这样做,我们有所有的游戏逻辑处理服务器端。 客户端只通过web套接字将他们的input发送到服务器,服务器用游戏中正在发生的事情更新客户端(也是web套接字)。 这是我们问题的开始。

所有的服务器端math都变得相当沉重,我们发现我们需要以某种方式扩展来处理超过10个玩家(我们希望能够承载更多)。 起初我们认为我们可以根据需要垂直扩展,但由于nodejs是单线程的,因此只能利用一个内核。 这意味着获得更强大的服务器将不会帮助解决这个问题。 我们唯一的解决scheme是水平扩展。

为什么我们在这里问

我们还没有find如何扩展nodejs游戏的好例子。 我们的用例是非常特别的,虽然我们已经尽我们所能做到了这一点,但我们真的可以从外部的意见和build议中受益

细节

我们已经在如何解决这个问题上投入了大量的思想。 我们已经研究了一个多星期了。 以下是我们迄今所做的一切:

四种types的服务器

我们正在将任务分成4个不同的“服务器types”。 每个人都将完成一个特定的任务。

代理服务器

代理服务器将坐在整个堆栈的前端,并成为唯一可以从互联网上直接访问的服务器(可能会有更多这样的服务器)。 它会有haproxy,它会将所有连接路由到Web服务器。 我们selecthaproxy是因为它具有丰富的function集,可靠性和几乎无与伦比的速度。

Web服务器

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

游戏服务器

游戏服务器将处理所有的游戏math和关于游戏的可变更新。 游戏服务器还与数据库服务器进行通信,以logging关于游戏中玩家的很酷的统计数据。 这将使用nodejs完成。

数据库服务器

数据库服务器将托pipe数据库。 这个部分实际上是最简单的,因为我们发现rethinkdb ,有史以来最酷的分贝。 这很容易扩展,奇怪的是,成为扩展应用程序的最简单的部分。

其他一些细节

如果您在整个调查过程中遇到困难,请仔细阅读,这是我们如何进行扩展的半精确图表。

如果你只是好奇,或者认为看看我们的游戏可能是有帮助的,那么目前它在这里是以非缩放状态托pipe的。

有些事情我们不想要

  • 我们不想使用nodejs的集群模块。 这是不稳定的( 在这里说),它不扩展到其他服务器,只有其他处理器。 我们希望能够跨越水平缩放。

我们的问题总结

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

谢谢

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

谢谢!!

遵循我对你的情况的自发想法:

多核使用

node.js也可以扩展到多个内核。 如何,你可以在这里阅读( 或者只是想一想:你有一个线程/进程在一个核心上运行,你需要使用多个核心?多个线程或多个进程将工作从主线程推送到其他线程或进程和你完成 )。

我个人会说开发一个不使用多核的应用程序是幼稚的。 如果你使用了一些后台进程,那么可以,但是如果你直到现在只能在node.js主事件循环中工作,那么你肯定应该投入一些时间来使应用程序在核心上扩展。

顺便说一下,像IPC这样的实现并不那么容易。 你可以做,但是如果你的情况很复杂,那么你可以使用集群模块。 这显然不是你最喜欢的,但只是因为有些东西被称为“实验”,并不意味着它是垃圾。 只要试一下,也许你可以在路上修正模块的一些错误。 使用一些广泛使用的软件来解决复杂的问题最有可能比创造新的车轮更好。

你也应该(如果你还没有)考虑(明智)使用nextTickfunction。 这允许主事件循环暂停一些cpu密集型任务,同时执行其他工作。 你可以阅读关于它的例子 。

关于计算的一般想法

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

你最好的select是使你的游戏代码优雅而快速。 想想如何有效地解决问题。 如果你不能有效地解决Javascript中的问题,但是这个问题很容易解决,为什么不写一个小的C组件呢? 这也算作一个独立的进程,这减less了你的主要node.js事件循环的负载。

代理?

就个人而言,我现在看不到代理级别的优势。 您似乎并不期望大量的用户,因此您不需要解决像CDN解决的问题或其他问题……可以考虑一下,但我现在不会花太多时间在那里。

从技术上讲,无论如何,您的web服务器软件提供代理function的可能性很高。 所以可以把它放在纸上,但是现在我不打算用专用硬件。

结语

其余的对我来说似乎或多或less。

游戏迟到了,但看看这里: http : //goldfirestudios.com/blog/136/Horizo​​ntally-Scaling-Node.js-and-WebSockets-with-Redis

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

还有这个node插件: http : //blog.varunajayasiri.com/shared-memory-with-nodejs这让你在进程之间共享内存,但只能在Linux下工作。 如果您不希望始终通过本地进程发送数据,或者不得不处理nodes ipc api,这将有所帮助。

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

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

希望这可以帮助!

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