首页 > 解决方案 > 节点创建进程作为核心数

问题描述

来自 Node.JS 文档:

这些子节点仍然是 V8 的全新实例。假设每个新节点至少需要 30 毫秒的启动时间和 10 mb 的内存。也就是说,您不能创建成千上万个。

结论 最好的办法是按照 CPU 内核的数量进行分叉,即:

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('death', function(worker) {
    console.log('worker ' + worker.pid + ' died');
    cluster.fork();
  });
} else {
  // Worker processes have a http server.
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

但是,如果假设我们有 4 个内核,我们将创建 4 个进程 + 主进程,所以我们总共将有 5 个进程,这再次超过了 cpu 的内核。

这有效率吗?

标签: node.jsnode-cluster

解决方案


作为一般答案,您应该分叉尽可能多的进程,number of CPUs - 1因为您应该将一个核心留给 SO 来管理服务器上的其他进程(cron?logrotate?随便)。

此外,您 fork 的进程可以使用相同的 CPU,因为默认情况下,处理器关联由操作系统管理。(这里有一个自定义它的示例以及关于 stackoverflow 的一个很好的答案

因此,这主要取决于您的应用程序在做什么,因为分叉根本无法受益。用于高强度 CPU 工作的分叉(例如crypto或一些阻塞事件循环操作)将从这种优化中受益。

例如,使用您的脚本和一个简单的基准测试

npx autocannon -c 100 -d 5 -p 10 localhost:8000/

┌───────────┬────────┬────────┬─────────┬─────────┬─────────┬──────────┬────────┐
│ Stat      │ 1%     │ 2.5%   │ 50%     │ 97.5%   │ Avg     │ Stdev    │ Min    │
├───────────┼────────┼────────┼─────────┼─────────┼─────────┼──────────┼────────┤
│ Req/Sec   │ 46943  │ 46943  │ 71039   │ 79039   │ 68444.8 │ 11810.39 │ 46930  │
├───────────┼────────┼────────┼─────────┼─────────┼─────────┼──────────┼────────┤
│ Bytes/Sec │ 6.1 MB │ 6.1 MB │ 9.23 MB │ 10.3 MB │ 8.9 MB  │ 1.54 MB  │ 6.1 MB │
└───────────┴────────┴────────┴─────────┴─────────┴─────────┴──────────┴────────┘

然后将相同的 autocannon 脚本发送到没有 fork 的端点:

var http = require('http')

// Worker processes have a http server.
http.Server(function (req, res) {
  res.writeHead(200)
  res.end('hello world\n')
}).listen(8000)
┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬─────────┬─────────┐
│ Stat      │ 1%      │ 2.5%    │ 50%    │ 97.5%   │ Avg     │ Stdev   │ Min     │
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
│ Req/Sec   │ 48063   │ 48063   │ 62303  │ 63167   │ 59632   │ 5807.42 │ 48040   │
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
│ Bytes/Sec │ 6.25 MB │ 6.25 MB │ 8.1 MB │ 8.21 MB │ 7.75 MB │ 755 kB  │ 6.25 MB │
└───────────┴─────────┴─────────┴────────┴─────────┴─────────┴─────────┴─────────┘

这里没有分叉并没有那么慢

但是,如果我们更改具有高 CPU 操作的端点:

http.Server(function (req, res) {
  res.writeHead(200)
  for (var i = 0; i < 999999; i++) {
    // cpu cycle waste
  }
  res.end('hello world\n')
}).listen(8000)

我们将无需分叉:

┌───────────┬────────┬────────┬────────┬────────┬────────┬─────────┬────────┐
│ Stat      │ 1%     │ 2.5%   │ 50%    │ 97.5%  │ Avg    │ Stdev   │ Min    │
├───────────┼────────┼────────┼────────┼────────┼────────┼─────────┼────────┤
│ Req/Sec   │ 1671   │ 1671   │ 1790   │ 1870   │ 1784.2 │ 76.02   │ 1671   │
├───────────┼────────┼────────┼────────┼────────┼────────┼─────────┼────────┤
│ Bytes/Sec │ 217 kB │ 217 kB │ 233 kB │ 243 kB │ 232 kB │ 9.88 kB │ 217 kB │
└───────────┴────────┴────────┴────────┴────────┴────────┴─────────┴────────┘

再配合fork+高强度CPU运算配合得到很大提升!!

┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
│ Stat      │ 1%      │ 2.5%    │ 50%     │ 97.5%   │ Avg     │ Stdev   │ Min     │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Req/Sec   │ 8575    │ 8575    │ 9423    │ 9823    │ 9324    │ 421.9   │ 8571    │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Bytes/Sec │ 1.12 MB │ 1.12 MB │ 1.22 MB │ 1.28 MB │ 1.21 MB │ 54.7 kB │ 1.11 MB │
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘

所以我认为你不应该“预先优化”一个 HTTP 端点,因为它可能会不费吹灰之力地做很多工作。


推荐阅读