Node.js и интенсивные запросы CPU

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

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

Как я понимаю, сервер будет очень быстрым для простых запросов веб-страниц, таких как просмотр списка пользователей или просмотр сообщения в блоге. Однако, если я хочу написать очень интенсивный код ЦП (например, в конце админа), который генерирует графику или изменяет размеры тысяч изображений, запрос будет очень медленным (несколько секунд). Поскольку этот код не является асинхронным, все запросы, поступающие на сервер в течение этих нескольких секунд, будут заблокированы до тех пор, пока мой медленный запрос не будет выполнен.

Одно из предложений заключалось в том, чтобы использовать Web Workers для задач с интенсивным использованием ЦП. Тем не менее, я боюсь, что веб-работникам будет сложно писать чистый код, так как он работает, включая отдельный JS файл. Что делать, если интенсивный код процессора находится в методе объекта? Это отстой, чтобы написать JS файл для каждого метода, который интенсивно работает в ЦП.

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

Любые предложения по преодолению этого (воспринимаемого) препятствия? Как вы пишете чистый объектно-ориентированный код с помощью Node.js, при этом убедитесь, что тяжелые задачи ЦП выполнены async?

Ответ 1

Вам нужна очередь задач! Перемещение ваших длинных задач из веб-сервера - это ХОРОШО. Сохранение каждой задачи в отдельном файле js способствует модульности и повторному использованию кода. Это заставляет задуматься о том, как структурировать вашу программу таким образом, чтобы облегчить ее отладку и поддержку в долгосрочной перспективе. Еще одно преимущество очереди задач - рабочие могут быть написаны на другом языке. Просто выполните задачу, выполните работу и напишите ответ.

что-то вроде этого https://github.com/resque/resque

Вот статья из github о том, почему они построили ее http://github.com/blog/542-introducing-resque

Ответ 2

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

EDIT: Я придумал аналогию; веб-приложение должно быть как ресторан. У вас есть официанты (веб-сервер) и повара (рабочие). Официанты находятся в контакте с клиентами и выполняют простые задачи, такие как предоставление меню или объяснение того, является ли какое-то блюдо вегетарианцем. С другой стороны, они делегируют более сложные задачи кухне. Поскольку официанты делают только простые вещи, они быстро реагируют, и повара могут сосредоточиться на своей работе.

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

Ответ 3

Пара подходов, которые вы можете использовать.

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

WebWorkers могут работать для ваших асинхронных процессов, но в настоящее время они не поддерживаются node.js. Существует несколько расширений, обеспечивающих поддержку, например: http://github.com/cramforce/node-worker

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

Ответ 4

Вы не хотите, чтобы ваш процессорный интенсивный код выполнял async, вы хотите, чтобы он выполнялся параллельно. Вам нужно получить обработку из потока, обслуживающего HTTP-запросы. Это единственный способ решить эту проблему. С NodeJS ответом является модуль кластера, для нереста дочерних процессов для тяжелого подъема. (AFAIK Node не имеет понятия потоков/разделяемой памяти, это процессы или ничего). У вас есть два варианта структурирования вашего приложения. Вы можете получить решение 80/20, создав 8 серверов HTTP и синхронно обрабатывая интенсивные вычисления для дочерних процессов. Это довольно просто. Вы могли бы потратить час, чтобы прочитать об этом по этой ссылке. На самом деле, если вы просто сорвите код примера вверху этой ссылки, вы получите 95% от этого пути.

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

Я удивлен, что ни один из этих других ответов даже не упоминает кластер.

Фон: Асинхронный код - это код, который приостанавливается до тех пор, пока что-то не произойдет где-то еще, и в этот момент код просыпается и продолжает выполнение. Один очень распространенный случай, когда что-то медленное должно происходить где-то еще, - это ввод-вывод.

Асинхронный код не полезен, если это ваш процессор, который отвечает за выполнение работы. Это точно так же, как "вычислить интенсивные" задачи.

Теперь может показаться, что асинхронный код является нишевым, но на самом деле он очень распространен. Это просто не полезно для интенсивных вычислений.

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

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