Node.js на многоядерных машинах

606

Node.js выглядит интересно, НО я должен что-то упустить - разве Node.js не настроен только на один процесс и поток?

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

Как Node.js вписывается в эту картину? Его идея как-то распространять несколько экземпляров или как?

zaharpopov
источник
4
Похоже, что Рия начинает серьезно относиться к включению встроенной многоядерной поддержки в узел: github.com/joyent/node/commit/…
broofa
2
Менеджер процессов PM2 использует модуль кластера для распространения приложений NodeJS на все доступные ядра: github.com/Unitech/pm2
Unitech,
@broofa, это не настоящие потоки и дочерние процессы не имеют общей памяти. Также посмотрите, что является эквивалентом Nodejs для реальных потоковых и volatile-статических переменных Java? ,
Pacerier

Ответы:

697

[ Это сообщение актуально по состоянию на 2012-09-02 (новее, чем указано выше). ]

Node.js действительно масштабируется на многоядерных машинах.

Да, Node.js - один поток на процесс. Это очень обдуманное проектное решение, устраняющее необходимость иметь дело с семантикой блокировки. Если вы не согласны с этим, вы, вероятно, еще не понимаете, насколько безумно сложно отлаживать многопоточный код. Для более глубокого объяснения модели процесса Node.js и почему она работает таким образом (и почему она НИКОГДА не будет поддерживать несколько потоков), прочитайте мой другой пост .

Итак, как мне воспользоваться преимуществами моего 16-ядерного блока?

Два пути:

  • Для больших сложных вычислительных задач, таких как кодирование изображений, Node.js может запускать дочерние процессы или отправлять сообщения дополнительным рабочим процессам. В этом проекте у вас был бы один поток, управляющий потоком событий, а N процессов, выполняющих сложные вычислительные задачи и пережевывающих остальные 15 процессоров.
  • Для масштабирования пропускной способности на веб-сервисе вы должны запустить несколько серверов Node.js на одном компьютере, по одному на ядро, и разделить трафик между ними. Это обеспечивает отличную привязку к процессору и масштабирует пропускную способность почти линейно с количеством ядер.

Масштабирование пропускной способности на веб-сервисе

Начиная с версии 6.0.X Node.js включил модуль кластера прямо из коробки, что упрощает настройку нескольких рабочих узлов, которые могут прослушивать один порт. Обратите внимание, что это НЕ то же самое, что более старый модуль "cluster" learnboost, доступный через npm .

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  http.Server(function(req, res) { ... }).listen(8000);
}

Рабочие будут бороться за принятие новых подключений, и, скорее всего, победит наименее загруженный процесс. Он работает довольно хорошо и может увеличить пропускную способность на многоядерном процессоре.

Если у вас достаточно нагрузки, чтобы позаботиться о нескольких ядрах, вы захотите сделать еще несколько вещей:

  1. Запустите службу Node.js за веб-прокси, таким как Nginx или Apache - что-то, что может регулировать соединение (если вы не хотите, чтобы условия перегрузки приводили к полному отключению), переписывать URL-адреса, обслуживать статический контент и прокси-сервер других вспомогательных служб.

  2. Периодически перерабатывайте ваши рабочие процессы. Для длительного процесса даже небольшая утечка памяти в конечном итоге приведет к увеличению.

  3. Настройка сбора журналов / мониторинга


PS: Есть обсуждение между Аароном и Кристофером в комментариях к другому посту (на момент написания статьи это самый верхний пост). Несколько комментариев по этому поводу:

  • Модель с общими сокетами очень удобна, поскольку позволяет нескольким процессам прослушивать один порт и конкурировать за новые подключения. Концептуально, вы могли бы подумать о том, что предварительно разветвленный Apache сделает это с существенным предупреждением, что каждый процесс примет только одно соединение, а затем умрет. Потеря эффективности для Apache приводит к накладным расходам на создание новых процессов и не имеет ничего общего с операциями с сокетами.
  • Для Node.js наличие N работников, конкурирующих в одном сокете, является чрезвычайно разумным решением. Альтернативой является настройка встроенного внешнего интерфейса, такого как Nginx, и передача этого прокси-трафика отдельным работникам, чередуя работников для назначения новых соединений. Два решения имеют очень похожие характеристики производительности. И поскольку, как я упоминал выше, вы, вероятно, захотите, чтобы Nginx (или его альтернатива) в любом случае стоял перед вашим сервисом узлов, выбор здесь действительно один:

Общие порты: nginx (port 80) --> Node_workers x N (sharing port 3000 w/ Cluster)

против

Индивидуальные порты: nginx (port 80) --> {Node_worker (port 3000), Node_worker (port 3001), Node_worker (port 3002), Node_worker (port 3003) ...}

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

Дейв Допсон
источник
1
Можете ли вы дать какие-либо советы по запуску разных сервисов на основе nodejs на одном компьютере? Например, скажем, у меня есть 1 сервер, и я хочу запустить myservice1.js на CpuCore1 и myservice2.js на CpuCore2. Могу ли я использовать кластер для этого? или это полезно только для создания клонированных сервисов?
UpTheCreek,
6
Вы должны отправить вопрос для этого! (и я скопирую этот комментарий как ваш первый ответ). То, что вы хотите сделать, на самом деле очень просто. Вам не нужен «кластер», вы просто запускаете два разных узла. Два скрипта, два процесса, два порта. Например, у вас может быть serviceA listen на 3000 и serviceB listen на 3001. Каждая из этих служб может использовать «кластер», чтобы иметь 1+ рабочих и периодически перерабатывать их и т. Д. Затем вы можете настроить Nginx для прослушивания на порту 80 и пересылки на правильный сервис на основе входящего заголовка «Host» и / или пути URL.
Дейв Допсон
1
Спасибо. Я уже опубликовал соответствующий вопрос - вы в значительной степени описали то, что я имел в виду, но я не уверен в том, как нацеливать ядра процессора (при использовании чего-то вроде навсегда).
UpTheCreek,
Отличный ответ, ддопсон. Каков наилучший способ взаимодействия двух узловых процессов на одном компьютере? Есть ли более быстрый протокол, чем TCP, когда они находятся на одной машине?
winduptoy
1
@Serob_b - ну да. Запуск приложения Node.js на нескольких компьютерах очень распространен. Там нет библиотеки, необходимой для этого. Вы просто запускаете свой код на нескольких машинах и распределяете нагрузку между ними. Проектирование программного обеспечения таким образом, чтобы оно масштабировалось (т. Е. Оно сохраняло состояние в какой-либо службе внешних данных вместо сохранения состояния в памяти) - это ваша работа.
Дейв Допсон,
45

Одним из методов может быть запуск нескольких экземпляров node.js на сервере, а затем установка балансировщика нагрузки (предпочтительно неблокирующего, такого как nginx) перед ними.

Чандра Секар
источник
36
node.js примерно такой же быстрый, как и nginx, вы можете поставить балансировщик нагрузки node.js перед вашими серверами node.js, если вы тоже этого
захотите
26
Райан специально сказал не делать этого, пока узел не станет более стабильным. Лучший способ - запустить nginx перед узлом.
повторное решение
2
Что касается nginx перед узлом, он не решит некоторые проблемы, например, если у вас есть очередь в памяти. Экземпляры двух узлов не смогут получить доступ к очереди друг друга.
повторное решение
5
Кроме того, nginx не полностью поддерживает HTTP 1.1, поэтому такие вещи, как WebSockets, не могут быть проксированы.
ашристофер
2
@mikeal, resopollution - я сильно на стороне Nginx. Я многократно ломал Node.js (без трассировки стека, просто умирает). Я никогда не разбивал Nginx. Nginx из коробки настроен на все виды нормальных дросселей. Node.js по умолчанию будет продолжать принимать новые соединения вместо того, чтобы обслуживать существующие, пока ящик не опустится ... да, весь ящик; Я разбил ядро ​​на коробке CentOS5 с помощью стресс-тестирования Node (теперь ЭТО на самом деле не должно происходить). Я немного пришел в себя и вижу светлое будущее для Node, возможно, включая выделенные роли типа LB. Просто еще нет.
Дэйв Допсон
30

Райан Даль отвечает на этот вопрос в техническом докладе, который он дал в Google прошлым летом. Перефразируя, «просто запустите несколько узловых процессов и используйте что-то разумное, чтобы позволить им обмениваться данными. Например, IPC в стиле sendmsg () или традиционный RPC».

Если вы хотите испачкать руки, проверьте модуль spark2 Forever . Это упрощает порождение процессов с несколькими узлами. Он управляет настройкой общего доступа к портам, поэтому каждый из них может принимать подключения к одному и тому же порту, а также автоматически перезапускается, если вы хотите убедиться, что процесс перезапускается, если / когда он умирает.

ОБНОВЛЕНИЕ - 10/11/11 : Консенсус в сообществе узлов, похоже, заключается в том, что кластер в настоящее время является предпочтительным модулем для управления несколькими экземплярами узлов на одном компьютере. Навсегда тоже стоит посмотреть.

broofa
источник
8
Forever и Cluster делают совершенно разные вещи. Вы могли бы даже использовать оба. Навсегда перезапускает процесс, когда он умирает. Кластер управляет несколькими работниками. Вы бы использовали Forever для управления своим мастер-процессом ...
Дейв Допсон,
4
Кроме того, модуль learnboost в значительной степени вытеснен версией Cluster, встроенной в Node v0.6.x (предупреждение: поверхность API действительно отличается)
Дейв Допсон,
@broofa Как IPC по умолчанию сравнивается с, скажем, использованием Redis или Memcache, просто отправляя строку / данные / массивы между процессами? Какой путь будет быстрее?
NiCk Newman
1
@broofa, IPC имеет огромные накладные расходы по сравнению с реальной общей памятью, на которую способны Java и C.
Pacerier
@Pacerier Правда, но совместно используемая память решает только проблему масштабирования в контексте одного хоста, не затрагивая проблемы макросов, необходимые для масштабирования между многими хостами. Т.е. как запустить в облаке.
Брооф
20

Вы можете использовать кластерный модуль. Проверьте это .

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {
    // Workers can share any TCP connection
    // In this case its a HTTP server
    http.createServer(function(req, res) {
        res.writeHead(200);
        res.end("hello world\n");
    }).listen(8000);
}
Сергей Жуков
источник
13

Многоузловая система использует все имеющиеся у вас ядра.
Посмотрите на http://github.com/kriszyp/multi-node .

Для более простых нужд вы можете запустить несколько копий узла на разных номерах портов и поставить балансировщик нагрузки перед ними.

CyberFonic
источник
12

Узел Js поддерживает кластеризацию, чтобы использовать все преимущества вашего процессора. Если вы не используете его с кластером, то, вероятно, вы тратите свои аппаратные возможности.

Кластеризация в Node.js позволяет создавать отдельные процессы, которые могут использовать один и тот же порт сервера. Например, если мы запустим один HTTP-сервер на порту 3000, это будет один сервер, работающий в одном потоке на одном ядре процессора.

Код, показанный ниже, позволяет кластеризовать ваше приложение. Этот код является официальным кодом, представленным Node.js.

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        console.log("I am running with ID : " + cluster.workers[id].process.pid);
    });

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {

    //Do further processing.
}

проверьте эту статью для полного урока

Туй
источник
11

Как упоминалось выше, Cluster будет масштабировать и распределять нагрузку на ваше приложение по всем ядрам.

добавив что-то вроде

cluster.on('exit', function () {
  cluster.fork();
});

Перезапустит любых провальных работников.

В наши дни многие люди также предпочитают PM2 , который обрабатывает кластеризацию для вас, а также предоставляет некоторые интересные функции мониторинга .

Затем добавьте Nginx или HAProxy перед несколькими компьютерами, работающими с кластеризацией, и у вас будет несколько уровней отработки отказа и гораздо более высокая нагрузочная способность.

Уилл Стерн
источник
3
PM2 отлично подходит для производственного использования. Инструменты мониторинга помогли мне решить проблемы с памятью в приложениях.
Мбокил
7

Будущая версия узла позволит вам раскошелиться на процесс и передать ему сообщения, и Райан заявил, что хочет найти какой-то способ обмена файловыми обработчиками, так что это не будет прямой реализацией Web Worker.

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

Mikeal
источник
7

Spark2 основан на Spark, который больше не поддерживается. Кластер является его преемником, и у него есть несколько интересных функций, таких как создание одного рабочего процесса на ядро ​​процессора и восстановление мертвых рабочих.

TheDeveloper
источник
Первоначальному вопросу и многим из этих ответов уже несколько месяцев, и поскольку узел движется так быстро, я ценю, что вы добавили объявление о кластере. Посмотрев на Cluster и его примеры, он выглядит именно так, как я (или OP?) Хочу для Node, спасибо!
Рияд Калла
5

Я использую Node worker для простого запуска процессов из моего основного процесса. Кажется, работает отлично, пока мы ждем официального пути.

christkv
источник
1
почему узел работающий example.js не может быть запущен, мой узел версии 0.3.3 pre
guilin 桂林
5

Новый ребенок на блоке здесь - «Up» в LearnBoost .

Он обеспечивает «перезагрузки с нулевым временем простоя» и дополнительно создает несколько рабочих (по умолчанию количество процессоров, но оно настраивается), чтобы обеспечить лучшее из всех миров.

Он новый, но, кажется, довольно стабильный, и я успешно использую его в одном из моих текущих проектов.

Рой
источник
5

Модуль кластера позволяет вам использовать все ядра вашей машины. Фактически, вы можете воспользоваться этим всего за 2 команды, не касаясь своего кода, используя очень популярный менеджер процессов pm2 .

npm i -g pm2
pm2 start app.js -i max
Алистер
источник
4

Вы можете запустить ваше приложение node.js на нескольких ядрах, используя модуль кластера в сочетании с модулем os, который можно использовать для определения того, сколько у вас процессоров.

Например, давайте представим, что у вас есть serverмодуль, который запускает простой http-сервер на сервере, и вы хотите запустить его для нескольких процессоров:

// Dependencies.
const server = require('./lib/server'); // This is our custom server module.
const cluster = require('cluster');
const os = require('os');

 // If we're on the master thread start the forks.
if (cluster.isMaster) {
  // Fork the process.
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }
} else {
  // If we're not on the master thread start the server.
  server.init();
}

Алексей Трехлеб
источник
0

Также возможно спроектировать веб-сервис как несколько автономных серверов, которые слушают сокеты Unix, так что вы можете использовать такие функции, как обработка данных, в отдельных процессах.

Это похоже на большинство архитектур веб-серверов scrpting / database, где процесс cgi обрабатывает бизнес-логику, а затем передает и извлекает данные через сокет unix в базу данных.

разница в том, что обработка данных записывается как веб-сервер узла, прослушивающий порт.

это более сложно, но в конечном итоге это то, куда должна идти разработка многоядерности. многопроцессная архитектура, использующая несколько компонентов для каждого веб-запроса.

Огненная ворона
источник
0

Можно масштабировать NodeJS до нескольких блоков, используя чистый балансировщик нагрузки TCP (HAProxy) перед несколькими блоками, запускающими по одному процессу NodeJS каждый.

Если у вас есть общие знания, которыми вы можете поделиться между всеми экземплярами, вы можете использовать центральное хранилище Redis или подобное, к которому можно получить доступ из всех экземпляров процесса (например, из всех блоков).

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