【问题标题】:What is the best way I can scale my nodejs app?我可以扩展我的 nodejs 应用程序的最佳方式是什么?
【发布时间】:2014-03-25 17:27:36
【问题描述】:

基础知识

现在我和我的几个朋友正在尝试开发一个用 nodejs 制作的浏览器游戏。这是一款多人自上而下的射击游戏,大部分客户端和服务器端代码都使用 JavaScript。我们有一个很好的总体方向,我们想进入,我们在开发游戏时获得了很多乐趣。制作这款游戏​​时,我们的目标之一是让作弊变得尽可能困难。这样做,我们所有的游戏逻辑都在服务器端处理。客户端仅通过 Web 套接字将其输入发送到服务器,而服务器会根据游戏中发生的情况更新客户端(也是 Web 套接字)。这是我们问题的开始。

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

我们为什么在这里问

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

详情

我们已经对如何解决这个问题进行了很多思考。我们已经为此工作了一个多星期。到目前为止,我们汇总了以下内容:

四种服务器

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

代理服务器

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

网络服务器

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

游戏服务器

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

数据库服务器

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

其他一些细节

如果您无法理解我们的整个设置,look at this,这是一张关于我们认为我们将如何扩展的半准确图表。

如果您只是好奇,或者认为看看我们的游戏可能会有所帮助,它目前在此处以未缩放的状态托管。

一些我们不想要的东西

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

我们的问题,总结

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

谢谢

我知道这是一个很长的问题,想出一个深思熟虑的答案并不容易,但我真的很感激。

谢谢!!

【问题讨论】:

  • 也许你可以利用cluster一篇好文章here
  • 我想知道这些年来这是怎么做到的。这是我目前正在尝试解决的问题。
  • 你可能想看看 PM2. pm2.keymetrics.io

标签: javascript node.js sockets scalability autoscaling


【解决方案1】:

按照我对您案件的自发想法:

多核使用

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

我个人会说开发一个不使用多核的应用程序是幼稚的。如果您使用一些后台进程,可以,但是如果您到现在为止只在 node.js 主事件循环中工作,那么您绝对应该花一些时间来使应用程序在内核上可扩展。

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

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

关于计算的一般想法

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

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

代理?

我个人目前看不到代理级别的优势。您似乎并不期望有大量用户,因此您不需要解决 CDN 解决的问题或其他任何问题……考虑一下没关系,但我现在不会在那里投入太多时间。

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

结语

其余的对我来说似乎或多或少都不错。

【讨论】:

  • 您提出了很多有趣的观点,但我认为这不是我们想要的。我们确实知道 nodejs 可以通过 cluster 模块使用多个核心,但我们不想使用它,并且我们有我们的理由(主要是它会使我们的代码进一步复杂化,并且不会产生比拥有许多更好的结果单核机器。我们现在可以进行水平扩展,也可以使用集群,但无论如何我们必须在以后水平扩展,所以我们现在应该这样做)。您确实有其他优点,但他们提出的问题多于答案。还是谢谢你!
【解决方案2】:

游戏有点晚了,但请看这里: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 绑定任务。例如,玩家可以在我的动作角色扮演游戏中杀死怪物并获得相当多的战利品。我有一个名为LootGenerater 的子进程,基本上每当玩家杀死怪物时,它都会通过默认的IPC api.send 将游戏idmob_iduser_id 发送到该进程。一旦子进程收到它,它就会遍历大型战利品表并管理项目(存储到 redis 或其他)并将其返回。

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

希望这会有所帮助!

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 2012-10-05
    • 2017-09-27
    • 2013-01-29
    • 1970-01-01
    • 2020-09-20
    相关资源
    最近更新 更多