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


605

Node.js виглядає цікаво, Але я повинен щось пропустити - чи не Node.js налаштований лише на один процес і потоки?

Тоді як він масштабується для багатоядерних процесорів та багатопроцесорних серверів? Зрештою, все чудово зробити максимально швидким однопотоковий сервер, але для великих навантажень я хотів би використовувати декілька процесорів. І те ж саме стосується того, щоб робити програми швидше - здається, сьогодні спосіб полягає у використанні декількох процесорів та паралелізації завдань.

Як Node.js вписується в цю картину? Чи є його ідея якось розподілити кілька примірників чи що?


4
Схоже, Ryah починає серйозно ставитися до включення вбудованої багатоядерної підтримки у вузол: github.com/joyent/node/commit/…
broofa

2
Менеджер процесів PM2 використовує кластерний модуль внутрішньо, щоб розповсюдити свої програми NodeJS на всі наявні ядра: github.com/Unitech/pm2
Unitech

@broofa, це не справжні теми, і дочірні процеси не мають спільної пам'яті. Дивіться також Який еквівалент Nodejs реальної нитки Java та мінливих статичних змінних? .
Pacerier

Відповіді:


696

[ Ця публікація оновлена ​​станом на 2012-09-02 (новіше, ніж вище). ]

Node.js абсолютно має масштаб на багатоядерних машинах.

Так, Node.js - це один потік на процес. Це дуже обдумане дизайнерське рішення і позбавляє від необхідності розібратися з фіксацією семантики. Якщо ви не згодні з цим, ви, мабуть, ще не усвідомлюєте, наскільки шалено важко налагоджувати багатопотоковий код. Для більш глибокого пояснення моделі процесу Node.js і чому вона працює таким чином (і чому вона НІКОЛИ не підтримуватиме декілька потоків) читайте в іншому моєму дописі .

Тож як я можу скористатися своєю 16 ядерною коробкою?

Два способи:

  • Для великих важких завдань обчислення, таких як кодування зображень, Node.js може запускати дочірні процеси або надсилати повідомлення додатковим робочим процесам. У цьому дизайні ви мали б один потік, який керує потоком подій та N процесів, виконуючи важкі обчислювальні завдання та пережовуючи інші 15 процесорів.
  • Для масштабування пропускної здатності у веб-службі слід запустити кілька серверів Node.js в одному вікні, по одному на ядро ​​та розділити трафік запиту між ними. Це забезпечує чудову спорідненість з процесором і дозволить масштабувати пропускну здатність майже лінійно з кількістю ядер.

Пропускна здатність масштабування на веб-сервісі

Оскільки v6.0.X Node.js включив модуль кластера прямо з вікна, що спрощує налаштування декількох працівників вузлів, які можуть слухати на одному порту. Зауважте, що це НЕ те саме, що і старший модуль "кластерного" навчання, доступний через 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
Ви можете запропонувати будь-які поради щодо запуску різних служб на базі вузлів на одному полі? Наприклад, скажіть, що у мене є 1 сервер, і я хочу запустити myservice1.js на CpuCore1 та myservice2.js на CpuCore2. Чи можу я використовувати для цього кластер? або це корисно лише для створення клонованих служб?
UpTheCreek

6
Ви повинні залишити питання для цього! (і я скопіюю цей коментар як вашу першу відповідь). Те, що ви хочете зробити, насправді насправді дуже просто. Вам не дуже потрібен "кластер", ви просто запустили два різні сервісні вузли. Два сценарії, два процеси, два порти. Наприклад, у вас може бути послугаAA прослуховування на 3000, а serviceB прослуховування на 3001. Кожна з цих служб може використовувати "кластер", щоб мати 1+ працівників і періодично їх переробляти тощо. Тоді ви можете налаштувати Nginx на прослуховування на порт 80 і переслати на правильний сервіс на основі вхідного заголовка "Хост" та / або URL-адреси.
Дейв Допсон

1
Дякую. Я вже розмістив відповідне запитання - ви майже описали те, що я мав на увазі, але я не впевнений у тому, як орієнтуватися на ядра CPU (при використанні чогось подібного назавжди).
UpTheCreek

Чудова відповідь ddopson. Який найкращий спосіб, щоб два вузлові процеси спілкувалися один з одним на одній машині? Чи є швидший протокол, ніж TCP, коли вони знаходяться на одній машині?
winduptoy

1
@Serob_b - ну так. Запуск програми Node.js на декількох машинах є дуже поширеним явищем. Для цього не потрібна бібліотека. Ви просто запускаєте свій код на декількох машинах і розподіляєте навантаження між ними. Архітектура вашого програмного забезпечення таким чином, щоб воно масштабувало (тобто воно зберігає стан у якомусь зовнішньому сервісі даних, а не зберігає стан в пам'яті) - це ваша робота.
Дейв Допсон

44

Одним із методів було б запустити кілька серверів node.js на сервері, а потім поставити перед ними балансир навантаження (бажано, що не блокує такий, як nginx).


36
node.js настільки ж швидкий, як nginx, ви можете поставити балансир завантаження node.js перед вашими серверами node.js, якщо ви хочете так само :)
mikeal

26
Райан спеціально сказав не робити цього, поки вузол не стане стабільнішим. Найкращий спосіб - запустити nginx перед вузлом.
повторне забруднення

2
що стосується nginx перед вузлом, він не вирішить певних проблем, наприклад, якщо у вас є черга в пам'яті. 2 екземпляри вузла не зможуть отримати доступ до черги один одного.
повторне забруднення

5
Крім того, nginx не підтримує HTTP 1.1 повністю, тому такі речі, як WebSockets, не можуть бути проксі.
ashchristopher

2
@mikeal, resopollution - Я сильно на стороні Nginx. Я декілька разів важко розбивав Node.js (без стека, просто гине). Я ніколи не розбивав Nginx. Nginx поза коробки налаштований з усіма видами розсудливих дроселів. Node.js за замовчуванням продовжуватиме приймати нові з'єднання, віддаючи перевагу обслуговуванню існуючих, поки вікно не знизиться ... так, весь ящик; Я зламав ядро ​​у вікні CentOS5 шляхом стрес-тестування Вузла (зараз цього дійсно не повинно статися). Я трохи обійшов, і бачу світле майбутнє для Node, потенційно включаючи виділені ролі типу LB. Тільки ще не.
Дейв Допсон

30

Ryan Dahl відповідає на це питання в технологічному розмові він дав в Google минулого літа. Якщо перефразовувати, "просто запустіть декілька вузлових процесів і використовуйте щось розумне, щоб дозволити їм спілкуватися. Наприклад, IPMS у стилі sendmsg () або традиційний RPC".

Якщо ви хочете відразу забруднити руки, перегляньте модуль spark2 Forever . Це робить нерест процесів з декількома вузлами тривіально простим. Він обробляє налаштування спільного доступу до портів, тому кожен може приймати з'єднання з тим самим портом, а також автоматично перезавантажувати, якщо ви хочете переконатись, що процес перезапущений, якщо / коли він загине.

ОНОВЛЕННЯ - 11.11.11 : Здається, що в спільноті вузлів є консенсус, що кластер зараз є кращим модулем для управління кількома екземплярами вузлів на машині. Назавжди варто подивитися.


8
Назавжди і Кластер роблять дуже різні речі. Ви можете навіть використовувати і те і інше. Назавжди перезапускає процес, коли він гине. Кластер управляє кількома працівниками. Ви б використовували Forever для управління своїм головним процесом ...
Дейв Допсон

4
Крім того, модуль Learboost значною мірою витіснений версією кластера, запеченою у Node v0.6.x (попередження: поверхня API не відрізняється)
Дейв Допсон,

@broofa Як IPC за замовчуванням порівняно з можливістю використовувати Redis або Memcache wile просто надсилає рядок / дані / масиви між процесами? Який шлях був би швидшим?
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 .

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


12

Node 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 чудово підходить для використання у виробництві. Засоби моніторингу допомогли мені вирішити проблеми з пам’яттю з додатками.
mbokil

7

Майбутня версія вузла дозволить роздвоювати процес і передавати йому повідомлення, а Райан заявив, що хоче знайти спосіб поділитися також файловими обробниками, тому це не буде простою реалізацією Web Worker.

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


7

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


Оригінальне запитання та багато цих відповідей - це кілька місяців, і так, щоб вузол рухався так швидко, я вдячний, що ви додали розмиття щодо кластеру. Переглянувши Кластер та його приклади, він виглядає саме так, що я (або ОП?) Хочу для Node, дякую!
Ріяд Калла

5

Я використовую працівника Node для запуску процесів простим способом з мого основного процесу. Здається, це працює чудово, поки ми чекаємо офіційного шляху.


1
Чому не можна запустити працівника node example.js, мій вузол має попередню версію 0.3.3
guilin

5

Новий хлопець на блоці тут - "Вгору" LearnBoost .

Він надає "Перезавантаження з нульовим простоєм" і додатково створює декількох працівників (за замовчуванням кількість процесорів, але це налаштовується), щоб забезпечити найкраще з усіх верств світу.

Він новий, але здається досить стабільним, і я його щасливо використовую в одному з моїх поточних проектів.


5

Модуль кластера дозволяє використовувати всі ядра вашої машини. Насправді ви можете скористатись цим лише у двох командах, не торкаючись коду, використовуючи дуже популярний менеджер процесів 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.