Node.js и ресурсоемкие запросы - PullRequest
202 голосов
/ 16 августа 2010

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

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

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

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

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

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

Ответы [ 5 ]

279 голосов
/ 16 августа 2010

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

РЕДАКТИРОВАТЬ: я придумал аналогию; веб-приложение должно быть как ресторан. У вас есть официанты (веб-сервер) и повара (рабочие). Официанты общаются с клиентами и выполняют простые задачи, такие как предоставление меню или объяснение того, является ли какое-то блюдо вегетарианским. С другой стороны, они делегируют более сложные задачи на кухню. Поскольку официанты делают только простые вещи, они быстро реагируют, а повара могут сосредоточиться на своей работе.

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

51 голосов
/ 21 августа 2010

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

как то так https://github.com/resque/resque

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

13 голосов
/ 21 февраля 2017

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

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

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

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

Асинхронный код бесполезен, если за выполнение этой работы отвечает ваш процессор . Именно так обстоит дело с «интенсивными вычислениями».

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

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

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

7 голосов
/ 23 августа 2010

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

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

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

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

0 голосов
/ 20 июня 2018

Использование child_process является одним из решений.Но каждый порожденный дочерний процесс может занимать много памяти по сравнению с Go goroutines

. Вы также можете использовать решение на основе очередей, например kue

...