node.js - 节点创建进程作为核心数
问题描述
来自 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 的内核。
这有效率吗?
解决方案
作为一般答案,您应该分叉尽可能多的进程,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 端点,因为它可能会不费吹灰之力地做很多工作。
推荐阅读
- git - 使用 Colab 提交到 Github 并得到:错误:pathspec 'commit'' did not match any file(s) known to git
- django - 使用 Django 的 github 操作上的迁移错误
- android - 每次变量更改时,如何从活动 B 更新活动 A 中的变量?
- c - 任务是使用 p 线程并行化矩阵乘法并使用英特尔 ISPC 编译器进行矢量化
- node.js - 无法将文件发送到 express/multer 后端
- angular - 将 http 响应映射到 typeScript 对象
- r - 将文本标签添加到 geom_smooth 平均线
- emacs - 移除 Emacs 小地图中的边缘箭头
- firebase - 如何使用云功能授权触发颤振/飞镖
- jquery - 使用 jquery 获取 3rd 方控件的 innerHTML 中存在的特定元素的值