如何限制(或排队)调用Node.JS中的外部进程?

脚本

我有一个Node.JS服务(使用ExpressJS编写),通过DnD( 示例 )接受图像上传。 上传图片后,我会做一些事情:

  1. 从中拉EXIF数据
  2. 调整它的大小

这些调用目前正在通过node-imagemagick模块进行处理,我的代码如下所示:

app.post('/upload', function(req, res){ ... <stuff here> .... im.readMetadata('./upload/image.jpg', function(err, meta) { // handle EXIF data. }); im.resize(..., function(err, stdout, stderr) { // handle resize. }); }); 

正如你们中的一些人已经发现的那样,问题是如果我有足够的同时上传,那么每一个上传的将会产生一个“身份”调用,然后resize操作(Image Magick),在高负载下有效地杀死服务器。

只要用ab -c 100 -n 100进行testing就可以locking我的小型512 Linode dev服务器,这样我就可以强制重启。 我知道我的testing对服务器来说可能只是太多的负载,但是我想要一个更稳健的方法来处理这些请求,所以我有一个更优雅的失败,那就是虚拟机自杀。

在Java中,我通过创build一个固定线程的ExecutorService来解决这个问题 ,该工作排队工作并在最多X个线程上执行它。

在Node.JS中,我甚至不知道从哪里开始解决这样的问题。 我没有把自己的大脑包装在非线程本质中,以及如何创buildasynchronousJavaScript函数来排队工作,而另一个…(线程?)处理队列。

任何关于如何思考这个或如何处理这个问题的指针将不胜感激。

附录

这与FFMpeg的这个问题不一样,尽pipe我认为一旦他的web应用程序处于加载状态,那么他就会有同样的问题,因为它归结为相同的问题(同时引发太多的并行本地进程)。

线程模块应该是你所需要的:

https://github.com/robtweed/threads

由于Node不允许线程,所以你可以在另一个进程中工作。 您可以使用后台作业系统(如resque) ,在这种系统中将要处理的作业排队到某种types的数据存储中,然后运行从数据存储中提取作业并执行处理的进程(或多个进程); 或者使用像node-worker这样的东西,并将你的工作排入工作者的内存。 无论哪种方式,您的主要应用程序都从所有处理中释放出来,并可以专注于提供Web请求。

[更新]另一个值得关注的库是hook.io ,特别是如果你喜欢节点工作者的想法,但想运行多个后台进程。 [/更新]

[编辑]

下面是一个快速和肮脏的例子,推动工作需要一段时间才能使用node-worker运行到工作进程; 工作人员排队工作并逐一处理:

app.js

 var Worker = require('worker').Worker; var processor = new Worker('image_processor.js'); for(var i = 0; i <= 100; i++) { console.log("adding a new job"); processor.postMessage({job: i}); } processor.onmessage = function(msg) { console.log("worker done with job " + msg.job); console.log("result is " + msg.data.result); }; 

image_processor.js

 var worker = require('worker').worker; var queue = []; worker.onmessage = function(msg) { var job = msg.job; queue.push(job); } var process_job = function() { if(queue.length == 0) { setTimeout(process_job, 100); return; } var job = queue.shift(); var data = {}; data.result = job * 10; setTimeout(function() { worker.postMessage({job: job, data: data}); process_job(); }, 1000); }; process_job(); 

对于任何一个认为布兰登的快速而又脏兮兮的人来说,这个变化已经不复存在,也没有不必要的忙碌。 我不能testing它,但它应该工作。

 var enqueue = function() { var queue = []; var execImmediate = function(fImmediate) { enqueue = function(fDelayed) queue.push(fDelayed); }; fImmediate(); var ic = setInterval(function() { var fQueued = queue.shift(); if (fQueued) { fQueued(); } else { clearInterval(ic); enqueue = execImmediate; } }, 1000); }; return execImmediate; }();