多核机器上的 Node.js

IT技术 javascript node.js node-cluster
2021-01-16 04:38:30

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

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

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

6个回答

[这篇文章截至 2012-09-02 是最新的(比上面的更新)。]

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

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

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

两种方式:

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

扩展 Web 服务的吞吐量

由于 v6.0.X Node.js 已经包含开箱即用的集群module,这使得设置可以在单个端口上侦听的多个节点工作程序变得容易。请注意,这与通过npm提供的较旧的 learnboost“集群”module不同

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 个 worker 竞争一个 socket 是一个非常合理的解决方案。另一种方法是设置一个像 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) ...}

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

@Serob_b - 嗯,是的。在多台机器上运行 Node.js 应用程序很常见。没有图书馆需要这样做。您只需在多台机器上运行您的代码并在它们之间分配负载。构建您的软件以使其可扩展(即,它将状态存储在某种外部数据服务中,而不是将状态保存在内存中)- 这就是您的工作。
2021-03-14 04:38:30
很好的答案 ddopson。让两个节点进程在同一台机器上相互通信的最佳方式是什么?当它们在同一台机器上时,是否有比 TCP 更快的协议?
2021-03-16 04:38:30
你能提供关于在一个盒子上运行不同的基于 nodejs 的服务的任何建议吗?例如,假设我有 1 个服务器,想在 CpuCore1 上运行 myservice1.js,在 CpuCore2 上运行 myservice2.js。我可以为此使用集群吗?还是只对创建克隆服务有用?
2021-03-25 04:38:30
你应该为此发布一个问题!(我会将此评论复制为您的第一个答案)。您想要做的实际上非常简单。你真的不需要“集群”,你只需要运行两个不同的节点服务。两个脚本、两个进程、两个端口。例如,您可以让 serviceA 侦听 3000 和 serviceB 侦听 3001。这些服务中的每一个都可能使用“集群”来拥有 1 个以上的工作人员并定期回收它们等。然后您可以配置 Nginx 以侦听端口 80 并转发到基于传入的“主机”标头和/或 URL 路径的正确服务。
2021-04-01 04:38:30
谢谢。我已经发布了一个相关的问题- 你几乎描述了我的想法,但我不确定如何定位 CPU 内核(当使用像永远这样的东西时)。
2021-04-03 04:38:30

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

ryan 特别说在节点更稳定之前不要这样做。最好的方法是在节点前运行 nginx。
2021-03-18 04:38:30
node.js 和 nginx 一样快,如果你愿意,你也可以在你的 node.js 服务器前面放置一个 node.js 负载均衡器:)
2021-03-26 04:38:30
至于节点前面的nginx,它不会解决某些问题,比如你有一个内存队列。2个节点实例将无法访问彼此的队列。
2021-04-03 04:38:30
同样,nginx 不完全支持 HTTP 1.1,因此不能代理 WebSockets 之类的东西。
2021-04-04 04:38:30
@mikeal,resopollution - 我强烈支持 Nginx。我多次使 Node.js 崩溃(没有堆栈跟踪,只是死了)。我从来没有崩溃过 Nginx。开箱即用的 Nginx 配置了各种合理的节流阀。默认情况下,Node.js 将继续接受新的连接而不是服务现有的连接,直到盒子出现故障……是的,整个盒子;我通过对 Node 进行压力测试使内核在 CentOS5 机器上崩溃(现在这真的不应该发生)。我已经了解了一些,我看到了 Node 的光明未来,可能包括专门的 LB 类型的角色。只是还没有。
2021-04-09 04:38:30

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

如果您想立即动手,请查看spark2 Forevermodule。它使生成多个节点进程变得轻而易举。它处理设置端口共享,因此它们每个都可以接受到同一端口的连接,并且如果您想确保进程在/当它死亡时重新启动,还可以自动重新生成。

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

Forever 和 Cluster 做的事情非常不同。您甚至可以同时使用两者。Forever 在进程终止时重新启动它。集群管理多个worker。你会使用 Forever 来管理你的主进程......
2021-03-13 04:38:30
@broofa 与仅在进程之间发送字符串/数据/数组的使用 Redis 或 Memcache 相比,默认 IPC 如何?哪种方式会更快?
2021-03-23 04:38:30
此外,learnboost module在很大程度上被 Node v0.6.x 中的 Cluster 版本所取代(警告:API 表面确实不同)
2021-03-25 04:38:30
@broofa,与 Java 和 C 能够做到的真实共享内存相比,IPC 有巨大的开销。
2021-03-25 04:38:30
@Pacerier 没错,但共享内存只解决了如何在单个主机的上下文中扩展的问题,而没有解决跨多个主机扩展所需的宏问题。即如何在云中运行。
2021-04-03 04:38:30

您可以使用集群module。检查这个

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);
}

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

查看本文以获取完整教程