多核计算机上的Node.js


605

Node.js看起来很有趣,但是我必须错过一些东西-Node.js是否仅调整为仅在单个进程和线程上运行?

那么它如何扩展到多核CPU和多CPU服务器?毕竟,使速度尽可能快的单线程服务器非常好,但是对于高负载,我想使用多个CPU。对于提高应用程序速度也是如此-今天看来,方法是使用多个CPU并使任务并行化。

Node.js如何适合这张图片?它的想法是以某种方式分发多个实例还是什么?


4
它看起来像Ryah开始认真对待包括内置多核支持节点:github.com/joyent/node/commit/...
broofa

2
PM2流程管理器在内部使用群集模块将您的NodeJS应用程序传播到所有可用的内核:github.com/Unitech/pm2
Unitech

@broofa,那些不是真正的线程,子进程没有共享内存。另请参阅与Java的真实线程和volatile-static变量等效的Node.js?
佩里耶

Answers:


696

[ 该帖子是截至2012-09-02的最新信息(比上述更新)。]

Node.js绝对可以在多核计算机上扩展。

是的,Node.js是每个进程一个线程。这是一个非常刻意的设计决策,无需处理锁定语义。如果您不同意这一点,您可能还没有意识到调试多线程代码有多么疯狂。有关Node.js流程模型以及它为何以这种方式起作用的原因(以及为什么它将永远不支持多个线程)的更深入的解释,请阅读我的其他文章

那么,如何利用我的16芯盒呢?

两种方式:

  • 对于像图像编码这样的繁重的计算任务,Node.js可以启动子进程或将消息发送到其他工作进程。在这种设计中,您需要一个线程来管理事件流,并由N个进程执行繁重的计算任务,并消耗另外15个CPU。
  • 为了扩展Web服务的吞吐量,您应该在一台机器上运行多个Node.js服务器,每个内核一个,并在它们之间分配请求流量。这提供了出色的CPU亲和力,并且将通过内核数几乎线性地扩展吞吐量。

在Web服务上扩展吞吐量

由于v.6.0.X Node.js 直接包含了群集模块,因此可以轻松设置可以在单个端口上侦听的多个节点工作程序。请注意,这与可通过npm获得的较早的learningboost“集群”模块不同。

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  http.Server(function(req, res) { ... }).listen(8000);
}

工人们将竞争接受新的连接,而负荷最小的过程最有可能获胜。它工作得很好,并且可以在多核设备上很好地扩展吞吐量。

如果您有足够的负载来关心多个内核,那么您将还要做一些其他事情:

  1. NginxApache之类的Web代理后面运行Node.js服务-可以进行连接限制(除非您希望过载条件完全关闭),重写URL,提供静态内容并代理其他子服务。

  2. 定期回收您的工作程序。对于长时间运行的过程,即使是很小的内存泄漏也会最终加起来。

  3. 设置日志收集/监控


PS:在另一篇文章的评论中,Aaron和Christopher之间进行了讨论(在撰写本文时,这是其最高文章)。关于此的一些评论:

  • 共享套接字模型非常方便,允许多个进程在单个端口上侦听并竞争接受新的连接。从概念上讲,您可以想到预先分支的Apache会这样做,但有一个重要的警告,即每个进程将只接受一个连接然后死亡。Apache的效率损失是在派生新进程的开销中,与套接字操作无关。
  • 对于Node.js,让N个工人在单个套接字上竞争是一个非常合理的解决方案。另一种选择是像Nginx一样设置一个机载前端,并将该代理流量代理给各个工作人员,在工作人员之间交替分配新的连接。两种解决方案具有非常相似的性能特征。而且,由于如上所述,您可能始终希望Nginx(或替代方法)在您的节点服务之前,因此这里的选择实际上是:

共享端口: nginx (port 80) --> Node_workers x N (sharing port 3000 w/ Cluster)

各个端口: nginx (port 80) --> {Node_worker (port 3000), Node_worker (port 3001), Node_worker (port 3002), Node_worker (port 3003) ...}

可以说,单个端口设置有一些好处(可能会减少进程之间的耦合,具有更复杂的负载平衡决策等),但是设置绝对是更多的工作,并且内置群集模块的成本很低适用于大多数人的-complexity替代方案。


1
您能提供关于在一个盒子上运行不同的基于nodejs的服务的任何建议吗?例如,假设我有1台服务器,并且想在CpuCore1上运行myservice1.js,并在CpuCore2上运行myservice2.js。我可以使用集群吗?还是仅对创建克隆服务有用?
UpTheCreek 2012年

6
您应该为此提一个问题!(我将将此评论复制为您的第一答案)。您实际上想要做的很简单。您实际上并不需要“集群”,只需运行两个不同的节点服务。两个脚本,两个进程,两个端口。例如,您可以让serviceA监听3000,而serviceB监听3001。每个服务都可能使用“集群”来拥有1个以上的工作线程并定期回收它们,等等。然后,您可以配置Nginx来监听端口80并转发到基于传入的“主机”标头和/或URL路径的正确服务。
戴夫·多普森

1
谢谢。我已经发布了一个相关的问题 -您已经描述了我的基本想法,但是我不确定如何定位CPU内核(当使用永久的东西时)。
UpTheCreek 2012年

很好的答案ddopson。使两个节点进程在同一台计算机上相互通信的最佳方法是什么?当它们在同一台计算机上时,是否有比TCP更快的协议?
winduptoy

1
@Serob_b-是的,是的。在多台机器上运行Node.js应用程序非常常见。不需要这样做的库。您只需在多台计算机上运行代码并在它们之间分配负载即可。对软件进行架构设计以使其可扩展(即,将状态存储在某种外部数据服务中,而不是将状态保存在内存中)-这就是您的工作。
戴夫·多普森

44

一种方法是在服务器上运行多个node.js实例,然后在它们前面放置一个负载平衡器(最好是像nginx这样的非阻塞式负载平衡器)。


36
node.js的运行速度与nginx
差不多,

26
ryan明确表示在节点更稳定之前不要这样做。最好的方法是在节点前面运行nginx。
resopollution

2
至于节点前面的nginx,它不会解决某些问题,例如是否有内存队列。2个节点实例将无法访问彼此的队列。
resopollution

5
同样,nginx不完全支持HTTP 1.1,因此无法代理WebSockets。
ashchristopher 2011年

2
@ mikeal,resopollution-我坚决支持Nginx。我多次硬性崩溃了Node.js(没有堆栈跟踪,只是死了)。我从没撞坏过Nginx。Nginx开箱即用的配置了各种理智的节流阀。默认情况下,Node.js会继续优先接受新连接,而不是为现有连接提供服务,直到该框出现故障为止。我通过对Node进行压力测试而使内核在CentOS5机器上崩溃(现在真的不应该发生)。我走了一些,我看到Node的光明前景,可能包括专用的LB型角色。只是还没有。
戴夫·多普森

30

瑞安·达尔(Ryan Dahl )在去年夏天在Google进行的技术演讲中回答了这个问题。简而言之,“只需运行多个节点进程并使用明智的方法进行通信即可,例如sendmsg()样式的IPC或传统的RPC”。

如果您想立即弄脏手,请查看spark2 Forever模块。它使生成多个节点进程变得异常容易。它负责设置端口共享,因此它们每个都可以接受到同一端口的连接,并且如果要确保进程终止,则还可以自动重生。

更新-11/11/11:节点社区的共识似乎是群集现在是用于管理每台计算机的多个节点实例的首选模块。 永远也值得一看。


8
Forever和Cluster所做的事情截然不同。您甚至可以同时使用两者。进程死后将永远重启。集群管理多个工作人员。您将使用Forever来管理您的主流程...
Dave Dopson'2

4
同样,Learnboost模块在很大程度上被替换为Node v0.6.x中的Cluster版本(警告:API的表面确实有所不同)
Dave Dopson 2012年

@broofa与使用Redis或Memcache只是在进程之间发送字符串/数据/数组的情况相比,默认IPC的情况如何?哪种方法会更快?
NiCk Newman

1
@broofa,与Java和C能够执行的实际共享内存相比,IPC具有巨大的开销。
Pacerier '17

@Pacerier是的,但是共享内存仅解决了如何在单个主机的上下文中扩展的问题,而没有解决跨多个主机扩展所需的宏问题。即如何在云中运行。
broofa

20

您可以使用群集模块。检查一下

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {
    // Workers can share any TCP connection
    // In this case its a HTTP server
    http.createServer(function(req, res) {
        res.writeHead(200);
        res.end("hello world\n");
    }).listen(8000);
}


12

Node Js支持群集以充分利用您的cpu。如果您不通过群集运行它,则可能是在浪费硬件功能。

Node.js中的群集允许您创建可以共享同一服务器端口的单独进程。例如,如果我们在端口3000上运行一台HTTP服务器,那么它就是在处理器单核上的单线程上运行的一台服务器。

下面显示的代码使您可以集群应用程序。该代码是由Node.js表示的正式代码。

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        console.log("I am running with ID : " + cluster.workers[id].process.pid);
    });

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {

    //Do further processing.
}

在这篇文章中查看完整的教程


11

如上所述,群集将在所有核心上扩展和平衡您的应用程序。

添加类似的东西

cluster.on('exit', function () {
  cluster.fork();
});

将重新启动所有失败的工作程序。

如今,许多人也更喜欢PM2,它可以为您处理集群并提供一些很酷的监视功能

然后,在运行群集的多台计算机前添加Nginx或HAProxy,您将拥有多个级别的故障转移和更高的负载容量。


3
PM2非常适合生产使用。监视工具帮助我解决了应用程序的内存问题。
mbokil

7

节点的未来版本将允许您派生一个进程并将消息传递给该进程,而Ryan表示他希望找到某种共享文件处理程序的方法,因此,它不是直接的Web Worker实现。

目前还没有一个简单的解决方案,但是还很早,node是我见过的发展最快的开源项目之一,因此希望在不久的将来有很棒的事情。


7

Spark2基于现在不再维护的Spark。群集是它的继承者,它具有一些很酷的功能,例如每个CPU内核产生一个工作进程,并重新产生死掉的工作程序。


最初的问题和许多这样的答案已有几个月的历史了,并且节点移动如此之快,我感谢您添加有关Cluster的文章。看着集群和它的实例后,它看起来正是像我(或OP?)要为节点,谢谢!
里亚德·卡拉

5

我正在使用Node worker从我的主流程以一种简单的方式运行流程。在我们等待正式发布之时,似乎工作做得很好。


1
为什么节点worker example.js无法运行,我的节点是0.3.3以前的版本
桂林桂林2010年

5

这里的新手是LearnBoost的“ Up”

它提供“零宕机时间重新加载”功能,并另外创建多个工作程序(默认情况下为CPU数量,但它是可配置的),以提供最佳性能。

它是新的,但似乎很稳定,我在当前的项目之一中愉快地使用它。


5

集群模块允许你利用你的机器的所有核心。实际上,您可以仅使用2个命令就可以利用此功能,而无需使用非常流行的流程管理器pm2修改代码。

npm i -g pm2
pm2 start app.js -i max

4

您可以通过将集群模块与os模块结合使用来在多个内核上运行node.js应用程序,该模块可用于检测您有多少个CPU。

例如,假设您有一个server在后端运行简单http服务器的模块,并且想要为多个CPU运行它:

// Dependencies.
const server = require('./lib/server'); // This is our custom server module.
const cluster = require('cluster');
const os = require('os');

 // If we're on the master thread start the forks.
if (cluster.isMaster) {
  // Fork the process.
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }
} else {
  // If we're not on the master thread start the server.
  server.init();
}


0

也可以将Web服务设计为几个侦听unix套接字的独立服务器,以便将诸如数据处理之类的功能推入单独的进程中。

这类似于大多数scrpring /数据库Web服务器体系结构,在该体系结构中,cgi进程处理业务逻辑,然后通过unix套接字将数据推入和拉出到数据库。

区别在于数据处理被编写为侦听端口的节点Web服务器。

它更加复杂,但是最终必须进行多核开发。一个针对每个Web请求使用多个组件的多进程体系结构。


0

使用纯TCP负载平衡器(HAProxy)在每个运行一个NodeJS进程的多个框前面可以将NodeJS扩展到多个框。

如果您随后在所有实例之间共享一些常识,则可以使用中央Redis商店或类似商店,然后可以从所有流程实例(例如,从所有包装箱中访问)


除非这些服务器中只有一个单核CPU,否则不会利用所有的CPU容量(除非您也正在做其他事情)。
UpTheCreek 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.