多核机器上的 Node.js

Node.js 看起来很有意思,但是我肯定漏掉了什么—— Node.js 不是只调优为运行在单个进程和线程上吗?

那么它如何扩展到多核 CPU 和多 CPU 服务器呢?毕竟,尽可能快地使用单线程服务器非常好,但是对于高负载,我希望使用多个 CPU。同样的道理也适用于加快应用程序的运行速度——如今的方法似乎是使用多个 CPU 并将任务并行化。

Js 是如何适应这种情况的? 它的想法是以某种方式分发多个实例还是什么?

247499 次浏览

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

node的未来版本将允许你fork一个进程并将消息传递给它,Ryan表示他想要找到一些共享文件处理程序的方法,所以它不会是一个直接的Web Worker实现。

目前还没有一个简单的解决方案,但它仍然处于早期阶段,node是我见过的移动最快的开源项目之一,所以期待在不久的将来会有一些很棒的东西。

也可以将web服务设计为几个独立的服务器,它们侦听unix套接字,这样就可以将数据处理等功能推到单独的进程中。

这类似于大多数脚本/数据库web服务器架构,其中cgi进程处理业务逻辑,然后通过unix套接字将数据推入和拉入数据库。

不同的是,数据处理被写入一个节点web服务器监听端口。

它更复杂,但最终是多核开发的必经之路。为每个web请求使用多个组件的多进程架构。

我使用节点工以一种简单的方式从我的主进程运行进程。在我们等待官方方式的时候,似乎工作得很好。

多节点利用您可能拥有的所有核心 看看http://github.com/kriszyp/multi-node.

对于更简单的需求,您可以在不同的端口号上启动多个节点副本,并在它们前面放置一个负载均衡器。

Ryan Dahl在去年夏天的他在谷歌上的技术演讲中回答了这个问题。换句话说,“只需运行多个节点进程,并使用一些合理的方法来允许它们进行通信。例如sendmsg()风格的IPC或传统的RPC”。

如果你想马上动手,请查看spark2 永远模块。它使得生成多个节点进程变得非常容易。它处理端口共享的设置,这样它们每个都可以接受到同一个端口的连接,如果你想确保一个进程在它死亡时重新启动,它也会自动重生

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

Spark2基于Spark,目前已不再维护。集群是它的继承者,它有一些很酷的特性,比如每个CPU核心生成一个工作进程,以及重生死亡的工作进程。

可以使用纯TCP负载均衡器(HAProxy)将NodeJS扩展到多个盒子,每个盒子运行一个NodeJS进程。

如果你有一些共同的知识在所有实例之间共享,你可以使用一个中央Redis存储或类似的,然后可以从所有流程实例访问(例如从所有盒子)

(# EYZ0)

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

是的,Node.js是一个线程一个进程。这是一个非常深思熟虑的设计决策,消除了处理锁定语义的需要。如果您不同意这一点,那么您可能还没有意识到调试多线程代码是多么困难。要了解Node.js进程模型的更深层次的解释,以及为什么它以这种方式工作(以及为什么它永远不支持多线程),请阅读我的另一篇文章

那么我如何利用我的16核盒子呢?

两种方式:

  • 对于像图像编码这样的大型繁重的计算任务,Node.js可以启动子进程或向其他工作进程发送消息。在这种设计中,有一个线程管理事件流,N个进程执行繁重的计算任务,并占用其他15个cpu。
  • 为了在web服务上扩展吞吐量,你应该在一个机器上运行多个Node.js服务器,每个核心一个,并在它们之间分配请求流量。这提供了出色的cpu亲和性,并将吞吐量几乎随核心数量线性扩展。

在web服务上扩展吞吐量

因为v6.0。X node .js已经直接包含了集群模块,这使得设置多个可以侦听单个端口的节点工作者变得很容易。注意,这与旧的learnboost“cluster”模块不一样,可以通过npm获得。

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

工人们将竞相接受新的连接,负载最少的流程最有可能胜出。它工作得非常好,可以在多核设备上很好地扩展吞吐量。

如果你有足够的负载来关心多个核,那么你也会想做更多的事情:

  1. 在web代理后面运行你的Node.js服务,比如NginxApache -可以做连接节流(除非你想要过载条件使盒子完全关闭),重写url,提供静态内容,并代理其他子服务。

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

  3. 设置日志收集/监控


PS:在另一篇文章的评论中有亚伦和克里斯托弗之间的讨论(写这篇文章时,它是第一篇文章)。这里有几点评论:

  • 共享套接字模型非常方便地允许多个进程侦听单个端口并竞争接受新连接。从概念上讲,您可以考虑使用预分叉的Apache来执行此操作,但需要注意的是,每个进程只接受单个连接,然后就会终止。对于Apache来说,效率的损失在于分叉新进程的开销,而与套接字操作无关。
  • 对于Node.js,让N个worker在一个套接字上竞争是一个非常合理的解决方案。另一种方法是建立一个像Nginx这样的盒式前端,并将代理流量分配给各个工作者,在工作者之间交替分配新连接。这两个解决方案具有非常相似的性能特征。因为,正如我上面提到的,你可能会想要Nginx(或替代方案)作为你的节点服务的前端,这里的选择实际上是:

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

vs

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

单个端口的设置可以说有一些好处(进程之间的耦合可能更少,有更复杂的负载平衡决策,等等),但它肯定需要更多的工作来设置,内置的集群模块是一个低复杂性的替代方案,适用于大多数人。

这个领域的新成员是LearnBoost的《飞屋环游记》

它提供“零停机重新加载”,并额外创建多个worker(默认为cpu数量,但可配置),以提供所有world中的最佳服务。

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

您可以使用集群模块。检查# EYZ1。

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);
}
如上所述,集群将在所有核上扩展和负载平衡你的应用 添加像

这样的东西
cluster.on('exit', function () {
cluster.fork();
});

将重新启动任何失败的工作。

现在,许多人也更喜欢PM2,它为您处理集群并提供一些很酷的监控功能

然后,在运行集群的几台机器前添加Nginx或HAProxy,你就有了多级故障转移和更高的负载能力。

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.
}

查看这篇文章完整的教程

集群模块允许您利用计算机的所有核心。事实上,您可以使用非常流行的进程管理器pm2,在不修改代码的情况下,仅用两个命令就可以利用这一点。

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

你可以通过使用集群模块和操作系统模块来在多核上运行你的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();
}

💢重要区别-滚动重启

我必须在使用节点的构建集群模式VS像PM2的集群模式的进程管理器之间添加重要的区别

PM2允许在运行时零停机时间重新加载。

pm2 start app.js -i 2 --wait-ready

在代码中添加以下内容

process.send('ready');
当你在代码更新后调用pm2 reload app时,PM2将重新加载 应用程序的第一个实例,等待'ready'调用,然后继续 要重新加载下一个实例,确保你总是有一个活跃的应用程序来响应请求

而如果你使用nodejs的集群,当你重新启动并等待服务器就绪时,将会有一段时间,因为应用程序只有一个实例,而你正在一起重新启动所有的核心。

我搜索了Clusterize一个应用程序所有可用的CPU内核,并在这里找到了我自己。我在哪里找到这个关键字是Pm2命令

pm2的例子

这是我的发现

  • 将一个应用聚类到所有可用的CPU内核:

    $ pm2 start -i Max

如果需要安装pm2,请使用这些命令

npm install -g pm2


yan add -g pm2

使用这个链接在这里