Как ограничить (или поставить в очередь) вызовы внешним процессам в Node.JS? - PullRequest
8 голосов
/ 03 сентября 2011

Сценарий

У меня есть служба 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.
  });
});

Вопрос

Как некоторые из вас ужепроблема в том, что если я получу достаточное количество одновременных загрузок, каждая из этих загрузок вызовет «идентификационный» вызов, а затем операцию изменения размера (из Image Magick), эффективно убивающую сервер при высокой нагрузке.

Просто тестирование с ab -c 100 -n 100 блокирует мой маленький 512 Linode dev server таким образом, что я вынужден перезагрузить компьютер.Я понимаю, что мой тест может быть слишком загружен для сервера, но я хотел бы более надежный подход к обработке этих запросов, чтобы у меня был более изящный сбой, чем полное самоубийство виртуальной машины.

В Java I решил эту проблему , создав службу ExecutorService с фиксированным потоком, которая ставит работу в очередь и выполняет ее не более чем в количестве потоков.

В Node.JS я даже не уверен, с чего начатьрешить проблему, как это.У меня не совсем получается, что мой мозг не связан с многопоточностью, и как я могу создать асинхронную функцию JavaScript, которая ставит работу в очередь, пока другой ... (поток?) Обрабатывает очередь.

Любые указателио том, как думать об этом или как подойти к этому, будет оценено.

Приложение

Это не то же самое, что этот вопрос о FFMpeg , хотя я предполагаю, что у этого человека будет точно такой же вопрос, как только его веб-приложение будет загружено, поскольку оно сводится к той же проблеме (запуск слишком большого количества параллельных собственных процессов параллельно).

Ответы [ 3 ]

2 голосов
/ 03 сентября 2011

Модуль потоков должен быть именно тем, что вам нужно:

https://github.com/robtweed/threads

2 голосов
/ 03 сентября 2011

Для тех, кто думал, что быстрое и грязное Брэндона может быть слишком быстрым и грязным, вот вариант, который больше не имеет ненужного ожидания занятости. Я не в состоянии проверить это, но оно должно работать.

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;
}();
2 голосов
/ 03 сентября 2011

Поскольку Node не разрешает многопоточность, вы можете выполнять работу в другом процессе. Вы можете использовать систему фоновых заданий, например resque , где вы помещаете в очередь задания для обработки в хранилище данных определенного типа, а затем запускаете процесс (или несколько процессов), который извлекает задания из хранилища данных и выполняет обработка; или используйте что-то вроде node-worker и поместите свои задания в рабочую память. В любом случае ваше основное приложение освобождается от всей обработки и может сосредоточиться на обслуживании веб-запросов.

[Обновить] Еще одна интересная библиотека, которую стоит проверить - это 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...