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

时间:2011-09-02 23:52:58

标签: javascript node.js queue

方案

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

  1. 从中提取EXIF数据
  2. 调整大小
  3. 目前正在通过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.
      });
    });
    

    问题

    正如你们中的一些人已经发现的那样,问题是,如果我获得足够的同步上传,那么每次上传都会产生一个“身份”调用然后调整大小操作(来自Image Magick),从而有效地杀死服务器负荷。

    只需使用ab -c 100 -n 100进行测试就可以锁定我的小型512 Linode dev服务器,这样我就必须强行重启。我知道我的测试可能对服务器来说负担过重,但我希望有一种更强大的方法来处理这些请求,这样我就会有更优雅的失败,然后总是自杀。

    在Java I solved this issue中创建一个固定线程的ExecutorService,它将工作排队并在最多X个线程上执行它。

    在Node.JS中,我甚至不确定从哪里开始解决这样的问题。我的大脑并没有完全围绕非线程特性以及我如何创建一个异步JavaScript函数来排队工作而另一个......(线程?)处理队列。

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

    附录

    这与this question about FFMpeg不同,虽然我想这个人在他的webapp处于负载状态时会有这个完全相同的问题,因为它归结为同样的问题(解雇了太多的同时本机进程)并行)。

3 个答案:

答案 0 :(得分:2)

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

[更新]另一个有趣的库是hook.io,特别是如果你喜欢node-workers的想法,但想要运行多个后台进程。 [/更新]

[编辑]

这是一个使用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();

答案 1 :(得分:2)

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

https://github.com/robtweed/threads

答案 2 :(得分:2)

对于那些认为布兰登的快速和肮脏可能快速和肮脏的人来说,这里的变化已经不再存在,也没有不必要的忙碌等待。我无法测试它,但它应该可以工作。

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