Javascript 将大型数组传递给节点子进程
我有复杂的CPU密集型工作要在大型阵列上完成。理想情况下,我希望将其传递给子进程Javascript 将大型数组传递给节点子进程,javascript,node.js,mongodb,Javascript,Node.js,Mongodb,我有复杂的CPU密集型工作要在大型阵列上完成。理想情况下,我希望将其传递给子进程 var spawn = require('child_process').spawn; // dataAsNumbers is a large 2D array var child = spawn(process.execPath, ['/child_process_scripts/getStatistics', dataAsNumbers]); child.stdout.on('data', function
var spawn = require('child_process').spawn;
// dataAsNumbers is a large 2D array
var child = spawn(process.execPath, ['/child_process_scripts/getStatistics', dataAsNumbers]);
child.stdout.on('data', function(data){
console.log('from child: ', data.toString());
});
但当我这样做时,node会给出错误:
产卵量大
我偶然发现
因此,将数据管道化到子进程似乎是一条可行之路。我的代码是:
var spawn = require('child_process').spawn;
console.log('creating child........................');
var options = { stdio: [null, null, null, 'pipe'] };
var args = [ '/getStatistics' ];
var child = spawn(process.execPath, args, options);
var pipe = child.stdio[3];
pipe.write(Buffer('awesome'));
child.stdout.on('data', function(data){
console.log('from child: ', data.toString());
});
然后在getStatistics.js中:
console.log('im inside child');
process.stdin.on('data', function(data) {
console.log('data is ', data);
process.exit(0);
});
但是,上的进程.stdin.中的回调未到达。如何在我的子脚本中接收流
编辑
我不得不放弃缓冲区方法。现在我以消息的形式发送数组:
var cp = require('child_process');
var child = cp.fork('/getStatistics.js');
child.send({
dataAsNumbers: dataAsNumbers
});
但这仅在DataAsNumber的长度低于20000时有效,否则它会超时。对于长流程任务,您可以使用类似于可以对工作人员执行繁重工作流程的方法,这样您可以设置需要多少工作人员,例如我以这种方式执行一些文件处理,如果我需要你创建更多的worker实例,我也有不同的worker用于不同的任务,处理zip文件,生成缩略图等等,这样做的好处是worker可以在任何语言node.js、Java、python上编写,并且可以轻松地集成到你的项目中
// worker-unzip.js
const debug = require('debug')('worker:unzip');
const {series, apply} = require('async');
const gearman = require('gearmanode');
const {mkdirpSync} = require('fs-extra');
const extract = require('extract-zip');
module.exports.unzip = unzip;
module.exports.worker = worker;
function unzip(inputPath, outputDirPath, done) {
debug('unzipping', inputPath, 'to', outputDirPath);
mkdirpSync(outputDirPath);
extract(inputPath, {dir: outputDirPath}, done);
}
/**
*
* @param {Job} job
*/
function workerUnzip(job) {
const {inputPath, outputDirPath} = JSON.parse(job.payload);
series([
apply(unzip, inputPath, outputDirPath),
(done) => job.workComplete(outputDirPath)
], (err) => {
if (err) {
console.error(err);
job.reportError();
}
});
}
function worker(config) {
const worker = gearman.worker(config);
if (config.id) {
worker.setWorkerId(config.id);
}
worker.addFunction('unzip', workerUnzip, {timeout: 10, toStringEncoding: 'ascii'});
worker.on('error', (err) => console.error(err));
return worker;
}
一个简单的index.js
const unzip = require('./worker-unzip').worker;
unzip(config); // pass host and port of the Gearman server
我通常用PM2管理工人
与代码的集成非常简单。差不多
//initialize
const gearman = require('gearmanode');
gearman.Client.logger.transports.console.level = 'error';
const client = gearman.client(configGearman); // same host and port
只需将工作添加到传递函数名称的队列中即可
const taskpayload = {inputPath: '/tmp/sample-file.zip', outputDirPath: '/tmp/unzip/sample-file/'}
const job client.submitJob('unzip', JSON.stringify(taskpayload));
job.on('complete', jobCompleteCallback);
job.on('error', jobErrorCallback);
我也能重现你所经历的延迟,但可能没有你那么糟糕。我使用了以下方法
// main.js
const fork = require('child_process').fork
const child = fork('./getStats.js')
const dataAsNumbers = Array(100000).fill(0).map(() =>
Array(100).fill(0).map(() => Math.round(Math.random() * 100)))
child.send({
dataAsNumbers: dataAsNumbers,
})
及
node main.js 2.72s用户0.45s系统103%cpu总计3.045
我正在生成由100个数字组成的100k元素来模拟您的数据,请确保您正在使用进程上的消息事件。但是,可能您的孩子更复杂,并且可能是失败的原因,这也取决于您对查询设置的超时
如果您想获得更好的结果,您可以将数据分块成多个片段,这些片段将被发送到子进程并重新构造以形成初始数组
还有一种可能是使用第三方库或协议,即使这需要更多的工作。您可以查看AMQP队列,甚至类似于AMQP队列,它允许您使用池在两个进程之间进行通信,并保证消息已被子进程确认。它有一些节点实现,例如,但仍然需要一些设置和配置工作。对于如此大量的数据,我会考虑使用而不是将数据复制到子进程中(这是使用管道或传递消息时发生的情况)。这将节省内存,减少父进程的CPU时间,并且不太可能遇到某些限制
是一个非常简单的模块,似乎适合您的应用程序。例如:
parent.js
"use strict";
const shm = require('shm-typed-array');
const fork = require('child_process').fork;
// Create shared memory
const SIZE = 20000000;
const data = shm.create(SIZE, 'Float64Array');
// Fill with dummy data
Array.prototype.fill.call(data, 1);
// Spawn child, set up communication, and give shared memory
const child = fork("child.js");
child.on('message', sum => {
console.log(`Got answer: ${sum}`);
// Demo only; ideally you'd re-use the same child
child.kill();
});
child.send(data.key);
"use strict";
const shm = require('shm-typed-array');
process.on('message', key => {
// Get access to shared memory
const data = shm.get(key, 'Float64Array');
// Perform processing
const sum = Array.prototype.reduce.call(data, (a, b) => a + b, 0);
// Return processed data
process.send(sum);
});
child.js
"use strict";
const shm = require('shm-typed-array');
const fork = require('child_process').fork;
// Create shared memory
const SIZE = 20000000;
const data = shm.create(SIZE, 'Float64Array');
// Fill with dummy data
Array.prototype.fill.call(data, 1);
// Spawn child, set up communication, and give shared memory
const child = fork("child.js");
child.on('message', sum => {
console.log(`Got answer: ${sum}`);
// Demo only; ideally you'd re-use the same child
child.kill();
});
child.send(data.key);
"use strict";
const shm = require('shm-typed-array');
process.on('message', key => {
// Get access to shared memory
const data = shm.get(key, 'Float64Array');
// Perform processing
const sum = Array.prototype.reduce.call(data, (a, b) => a + b, 0);
// Return processed data
process.send(sum);
});
请注意,我们只是通过IPC从父进程向子进程发送一个小的“密钥”,而不是整个数据。因此,我们节省了大量的内存和时间
当然,您可以将'Float64Array'
(例如双精度
)更改为应用程序所需的任何值。请注意,此库尤其只处理一维类型化数组;但是,这只是一个小的障碍。 使用内存中的缓存,并且让父进程用一些密钥存储数组内容,子进程将通过该密钥来重写内容。 您可以考虑使用OS管道作为节点子应用程序的输入。
我知道这并不是您想要的,但您可以使用该模块(包含在node中)。通过这种方式,您可以获得尽可能多的实例,以加快处理速度。此外,如果在开始处理之前不需要拥有所有可用的数据,可以考虑使用流。如果要处理的数据太大,我会将其存储在一个文件中,以便在处理过程中出现任何错误时可以重新初始化。
这里是一个集群的例子
var cluster = require('cluster');
var numCPUs = 4;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
console.log('id', worker.id)
}
} else {
doSomeWork()
}
function doSomeWork(){
for (var i=1; i<10; i++){
console.log(i)
}
}
var cluster=require('cluster');
var numpus=4;
if(cluster.isMaster){
对于(变量i=0;i 对于(var i=1;i为什么要创建子流程?跨子流程发送数据在cpu和实时性方面的成本可能高于在同一流程中进行处理所节省的成本
相反,我建议,对于超高效编码,您可以考虑在与NoDEJS主进程相同的内存中运行的工作者线程中进行统计计算。
您可以使用C++编写代码,您可以将其发送到工作线程,然后使用该工作线程将结果和事件返回到您的NoDEJS事件循环中。
这样做的好处是,你不需要额外的时间来把数据发送到不同的进程,但是缺点是你会为线程动作写一点C++代码,但是NNA扩展应该为你处理大部分的困难任务。se是一种多线程语言。该项目已完成90%,我现在不会从node更改。有很多文章解释了node的CPU使用率很高。通常,首先启动一个解决核心问题的项目是一个好主意。在多线程语言中,由于线程共享内存,您不需要复制数据。在这种情况下复制数据case会减慢一切。此外,当您将工作委托给libuv时,节点速度很快。如果您计划使用节点的v8部分进行繁重的处理,那么它不会很快。此外,如果出于任何原因,这是实际服务器的一部分,您的事件循环将阻塞,并且I/O将无法满足您的所有请求