Распараллеливание задач в Node.js
У меня есть некоторые задачи, которые я хочу сделать в JS, которые являются ресурсоемкими. Для этого вопроса давайте предположим, что они представляют собой тяжелые вычисления, а не системный доступ. Теперь я хочу одновременно запускать задачи A, B и C и выполнять некоторую функцию D, когда это будет сделано.
асинхронная библиотека предоставляет приятные строительные леса для этого:
async.parallel([A, B, C], D);
Если то, что я делаю, это просто вычисления, тогда это все равно будет выполняться синхронно (если только библиотека не ставит задачи на разные потоки, что я ожидаю, это не так). Как сделать это на самом деле параллельным? Что обычно делается с помощью асинхронного кода, чтобы не блокировать вызывающего абонента (при работе с NodeJS)? Это начинается с дочернего процесса?
Ответы
Ответ 1
Как сделать это фактически параллельным?
Во-первых, вы не будете работать параллельно в одном приложении node. Приложение node работает в одном потоке, и только одно событие за раз обрабатывается циклом событий node. Даже при работе в многоядерном ящике вы не получите parallelism обработки в приложении node.
Тем не менее, вы можете получить обработку parallelism на многоядерной машине путем разметки кода в отдельные процессы node или путем создания дочернего процесса. Это, по сути, позволяет создавать несколько экземпляров самого node и взаимодействовать с этими процессами по-разному (например, stdout, процесс fork IPC-механизм). Кроме того, вы можете отделить функции (от ответственности) к своему собственному приложению/серверу node и вызвать его через RPC.
Что обычно делается с помощью асинхронного кода, чтобы не блокировать вызывающего абонента (при работе с NodeJS)? Запускает ли он дочерний процесс?
Он не запускает новый процесс. Под когда async.parallel используется в node.js, он использует process.nextTick()
. А nextTick() позволяет избежать блокировки вызывающего абонента, откладывая работу на новый стек, чтобы вы могли чередовать интенсивные задачи процессора и т.д.
Короче говоря
Node не позволяет "из коробки" получить многопроцессорный concurrency. node вместо этого дает вам неблокирующий дизайн и цикл событий, который использует поток без обмена памятью. Несколько потоков не могут обмениваться данными/памятью, поэтому блокировки не нужны. node заблокирован. Один node процесс использует один поток, что делает node безопасным и мощным.
Когда вам нужно разделить работу между несколькими процессами, используйте какую-то передачу сообщений для связи с другими процессами/серверами.. IPC/RPC.
Подробнее см.
Удивительный ответ от SO на Что такое node.js... с тонны добра.
Понимание process.nextTick()
Ответ 2
Асинхронные и параллельные не то же самое. Асинхронный означает, что вам не нужно ждать синхронизации. Параллельно означает, что вы можете делать несколько вещей одновременно. Node.js является только асинхронным, но его единственным только 1 потоком. Он может работать только по одной вещи сразу. Если у вас длительное вычисление, вы должны начать другой процесс, а затем просто выполните процесс Node.js асинхронно дождаться результатов.
Для этого вы можете использовать child_process.spawn, а затем прочитать данные из stdin.
http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
var spawn = require('child_process').spawn;
var process2 = spawn('sh', ['./computationProgram', 'parameter'] );
process2.stderr.on('data', function (data) {
//handle error input
});
process2.stdout.on('data', function (data) {
//handle data results
});
Ответ 3
Имейте в виду, что I/O распараллеливается с помощью Node.js; только ваши обратные вызовы JavaScript однопоточные.
Предполагая, что вы пишете сервер, альтернативой добавлению сложности процессов нереста или форкинга является просто создание серверов без состояния node и запуск экземпляра на ядро или, еще лучше, запуск многих экземпляров на своем собственном виртуализованном микросервере, Координаты входящих запросов с использованием обратного прокси-сервера или балансировки нагрузки.
Вы также можете отключить вычисление на другом сервере, возможно, MongoDB (используя MapReduce) или Hadoop.
Чтобы быть по-настоящему хардкорным, вы можете написать плагин node на С++ и иметь мелкомасштабный контроль распараллеливания кода вычислений. Ускорение от С++ может в любом случае отрицать необходимость распараллеливания.
Вы всегда можете писать код для выполнения вычислительно-интенсивных задач на другом языке, наиболее подходящем для числовых вычислений, и, например, выставлять их через API REST.
Наконец, вы могли бы запустить код на графическом процессоре, используя node-cuda
или что-то подобное в зависимости от типа вычисления (не все могут быть оптимизированы для GPU).
Да, вы можете разветвлять и запускать другие процессы, но мне кажется, что одним из главных преимуществ node является не столько беспокоиться о распараллеливании и потоковом потоке, а потому вообще обойти огромную сложность.
Ответ 4
Совсем недавно наткнулся на parallel.js, но он, по-видимому, фактически использует многоядерные процессоры, а также имеет функции уменьшения размера карты.
http://adambom.github.io/parallel.js/
Ответ 5
В зависимости от вашего варианта использования вы можете использовать что-то вроде
task.js Упрощенный интерфейс для запуска кода интенсивного процессора на всех ядрах (node.js, и веб)
Пример:
function blocking (exampleArgument) {
// block thread
}
// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);
// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
// do something with result
});