Одновременное управление несколькими длительными задачами в JS (Node.js )

#javascript #node.js #multithreading #node-worker-threads

#javascript #node.js #многопоточность #узел-рабочие потоки

Вопрос:

Разработчик Golang здесь, пытается изучить JS (Node.js ).

Я привык работать с программами в Go, которые для простоты давайте предположим, что это просто потоки (на самом деле это не совсем потоки, больше похожие на зеленые потоки, но потерпите!).

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

Как я могу реализовать что-то подобное в JS? Я просмотрел Node.js Документация по API, и я нашел несколько интересных вещей:

  • Кластер: похоже, это не совсем то, что я ищу
  • Дочерние процессы: может сработать, но я бы создавал 1 процесс на клиента / пользователя, и накладные расходы были бы огромными, я думаю
  • Рабочие потоки: это больше похоже на это, но в документации указано, что они «полезны для выполнения операций JavaScript с интенсивным использованием процессора» и «Node.js встроенные асинхронные операции ввода-вывода более эффективны, чем могут быть рабочие»

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

Любой ввод или предложение будут оценены. Спасибо!

Ответ №1:

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

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

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

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

Просто добавьте прослушиватель событий в каждый WebSocket, и ваш сервер nodejs будет легко обслуживать нескольких пользователей. Когда пользователь отключает свой WebSocket, слушатель автоматически уходит с ним. В этом отношении больше нечего делать или очищать, если вы не хотите обновить внутреннее состояние, и в этом случае вы также можете прослушать disconnect событие.

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

Я не знаю программы, но есть много вариантов для хранения состояния пользователя. Если это просто информация, к которой вам нужно иметь доступ, когда у вас уже есть WebSocket, и вам не нужно, чтобы она сохранялась сверх этого, тогда вы можете просто добавить состояние непосредственно к объекту WebSocket. Этот объект будет доступен в любое время, когда вы получите событие WebSocket, поэтому вы всегда можете иметь его для обновления при поступлении входящих данных. Вы также можете поместить состояние в другие места (база данных, объект карты, который индексируется по сокету или по имени пользователя, независимо от того, что вам нужно, чтобы иметь возможность его искать) — это действительно зависит от того, что именно такое состояние.

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

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

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


Время, когда вы бы задействовали кластеризацию, дочерние процессы или рабочие потоки, — это когда у вас в вашем Javascript больше процессорной обработки для обработки входящих данных, чем может обработать одно ядро. Я бы пошел туда только в том случае, если / когда вы докажете, что у вас есть проблема с масштабируемостью при использовании ЦП на вашем сервере nodejs. Затем вы хотели бы создать архитектуру, которая добавляет всего несколько других процессов или потоков для совместного использования нагрузки (а не по одному на соединение). Если у вас есть определенные процессы с высокой нагрузкой на процессор (классическими примерами являются пользовательское шифрование или сжатие), то вы можете помочь создать несколько других процессов или рабочих потоков, которые просто обрабатывают рабочую очередь для работы с высокой нагрузкой на процессор. Или, если это просто увеличивает общие циклы процессора, доступные для обработки входящих данных, тогда вы, вероятно, перейдете к кластеризации и просто позволите каждому входящему веб-сокету назначаться кластеру и по-прежнему использовать ту же логику обработки событий, описанную ранее, но теперь у вас есть веб-сокеты, разделенные на несколько процессов, поэтому у вас больше процессорачтобы бросить на них.