Інтенсивні запити Node.js та процесора


215

Я почав займатися HTTP-сервером Node.js і дуже люблю писати сервер на Javascript, але щось не заважає мені починати використовувати Node.js для мого веб-додатку.

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

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

Одна з пропозицій полягала в тому, щоб використовувати Web Workers для завдань, що потребують процесора. Однак я боюся, що веб-працівникам буде складно написати чистий код, оскільки він працює, включивши окремий файл JS. Що робити, якщо інтенсивний код CPU знаходиться в методі об'єкта? Це відстійно, щоб написати файл JS для кожного методу, який є інтенсивним процесором.

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

Будь-які пропозиції подолати цю (сприйняту) перешкоду? Як ви пишете чистий об'єктно-орієнтований код з Node.js, переконуючись, що важкі завдання процесора виконуються асинхронізовано?


2
Олів'є, ти задав те саме питання, яке я мав на увазі (нове для вузла), а саме стосовно обробки зображень. У Java я можу використовувати ExecutorService з фіксованою ниткою і передавати всі завдання розміру і чекати, коли закінчиться все з'єднання, у вузлі я не зрозумів, як перенести роботу на зовнішній модуль, який обмежує (давайте скажімо) максимальна кількість одночасних операцій до 2 одночасно. Ви знайшли елегантний спосіб зробити це?
Ріяд Калла

Відповіді:


55

Що вам потрібно, це черга завдань! Переміщення довгих завдань із веб-сервера - ДУЖЕ. Утримання кожного завдання у "окремому" файлі js сприяє модульності та повторному використанню коду. Це змушує вас подумати про те, як структурувати свою програму таким чином, що полегшить налагодження та підтримку в довгостроковій перспективі. Ще однією перевагою черги завдань є те, що робітники можуть бути написані різною мовою. Просто спливіть завдання, зробіть роботу і напишіть відповідь назад.

щось подібне https://github.com/resque/resque

Ось стаття від github про те, чому вони побудували її http://github.com/blog/542-introducing-resque


35
Чому ви посилаєтесь на бібліотеки Ruby у питанні, спеціально обґрунтованому у світі вузлів?
Джонатан Дюман

1
@JonathanDumaine Це хороша реалізація черги завдань. Відпрацюйте код рубіну та перепишіть його у javascript. ПРИБУТЬ!
Simon Stender Boisen

2
Я великий прихильник екіпажу для цього, робітники-екіпажі не опитують сервер-редуктор на нові робочі місця - нові робочі місця моментально підштовхуються до працівників. Дуже чуйний
Кейсі Флінн

1
Насправді хтось переніс його у світ вузла: github.com/technoweenie/coffee-resque
FrontierPsycho

@pacerier, чому ти це кажеш? Що ви пропонуєте?
luis.espinal

289

Це нерозуміння визначення веб-сервера - його слід використовувати лише для "розмови" з клієнтами. Завдання з великим навантаженням слід делегувати окремим програмам (що, звичайно, можна також записати в JS).
Ви, напевно, скажете, що це брудно, але я запевняю вас, що процес веб-сервера, що застряг у зміні розмірів зображень, є ще гіршим (навіть, скажімо, Apache, коли він не блокує інші запити). Тим не менш, ви можете використовувати загальну бібліотеку, щоб уникнути надмірності коду.

EDIT: я придумав аналогію; веб-додаток має бути як ресторан. У вас є офіціанти (веб-сервер) та кухарі (робітники). Офіціанти контактують з клієнтами і виконують прості завдання, такі як надання меню або пояснення, чи яке блюдо вегетаріанське. З іншого боку, вони делегують більш важкі завдання кухні. Оскільки офіціанти роблять лише прості речі, вони швидко реагують, а кухарі можуть зосередитися на своїй роботі.

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


6
Що ж, в умовах, коли веб-сервери є багатопотоковими або багатопроцессорними і можуть обробляти більше одного одночасного запиту, дуже часто витрачається на один запит пару секунд. Люди прийшли очікувати цього. Я б сказав, що непорозуміння полягає в тому, що node.js є "звичайним" веб-сервером. Використовуючи node.js, ви повинні трохи відкоригувати свою модель програмування, а це включає підштовхування "тривалої" роботи до якогось асинхронного працівника.
Тило

13
Не породжуйте дочірній процес для кожного запиту (що перешкоджає призначенню node.js). Нерестові працівники зсередини лише ваші важкі запити. Або направляйте ваші важкі фонові роботи до чогось іншого, ніж node.js.
Тило

47
Гарна аналогія, mbq!
Ленс Фішер

6
Ха, мені це дуже подобається. "Node.js: шкідливі практики погано працюють"
етан

7
@mbq Мені подобається аналогія, але вона може використовувати деякі роботи. Традиційною багатопотоковою моделлю була б людина, яка є і офіціантом, і кухарем. Після того, як замовлення прийнято, ця людина повинна повернутися назад і приготувати страву, перш ніж мати змогу впоратися з іншим замовленням. Модель node.js має вузли як офіціанти, а веб-робітники - як кухарі. Офіціанти обробляють отримання / вирішення запитів, тоді як працівники впораються з більш трудомісткими завданнями. Якщо вам потрібно масштабувати масштаби, ви просто зробите основний сервер кластерним вузлом і реверсує проксі-сервер, що інтенсивно виконує завдання процесора, на інші сервери, побудовані для обробки з багатопотоковою обробкою.
Еван Плейс

16

Ви не хочете, щоб ваш інтенсивний код CPU виконував асинхронізацію, ви хочете, щоб він виконувався паралельно . Вам потрібно отримати роботу з обробки потоку, який обслуговує HTTP-запити. Це єдиний спосіб вирішити цю проблему. Відповідь NodeJS - це кластерний модуль, для нерестування дитячих процесів робити важкі підйоми. (У вузлі AFAIK немає жодної концепції потоків / спільної пам'яті; це процеси чи нічого). У вас є два варіанти структурування програми. Ви можете отримати рішення 80/20 шляхом нерестування 8 серверів HTTP та синхронної обробки обчислювальних завдань на дочірніх процесах. Робити це досить просто. Ви можете піти на годину, щоб прочитати про це за цим посиланням. Насправді, якщо ви просто зірвете код прикладу у верхній частині цього посилання, ви отримаєте 95% шляху туди.

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

Я здивований, що жоден з цих інших відповідей навіть не згадує кластер.

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

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

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

Очікування на введення-виведення - це, наприклад, завжди на веб-серверах. Кожен клієнт, який підключився до вашого сервера, отримує розетку. Більшу частину часу розетки порожні. Ви не хочете нічого робити, поки сокет не отримає певних даних, і тоді ви хочете обробити запит. Під кришкою сервер HTTP, як Node, використовує бібліотеку подій (libev) для відстеження тисяч відкритих сокетів. ОС повідомляє libev, а потім libev повідомляє NodeJS, коли один з сокетів отримує дані, і тоді NodeJS ставить подію в чергу подій, і ваш http-код стартує в цей момент і обробляє події одна за одною. Події не ставлять у чергу, поки сокет не має деяких даних, тому події ніколи не чекають на дані - вони вже є для них.

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


повинна бути правильна відповідь .... що стосується рішення, де ви породжуєте 8 кластерів, вам знадобиться 8 ядер? Або завантажте балансир з декількох серверів.
Мухаммед Умер

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

Це вірно. Вам потрібно якось перенести твір на інше ядро. Для цього вам потрібно ще одне ядро.
масон

Re: черги. Практична відповідь - використання черги на роботу. Є кілька доступних для вузла. Я ніколи не використовував жодного з них, тому не можу дати рекомендацію. Відповідь цікавості полягає в тому, що робочі процеси та процеси черги в кінцевому рахунку збираються спілкуватися через сокети.
масон

7

Пара підходів, якими ви можете скористатися.

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

WebWorkers можуть працювати для ваших асинхронних процесів, але їх наразі не підтримує node.js. Є кілька розширень, які надають підтримку, наприклад: http://github.com/cramforce/node-worker

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


0

Використання child_process- це одне рішення. Але кожен породжений процес дитини може зайняти багато пам'яті порівняно з Gogoroutines

Ви також можете використовувати рішення на основі черги, такі як kue

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.