Понимание Node.js параллельного процесса и переменной области - PullRequest
0 голосов
/ 25 апреля 2020

У меня есть приложение Express. js, к которому я сейчас добавляю поддержку кластера Node с помощью Throng. Мне не удалось полностью сосредоточиться на том, как процессы должны работать с кластерами. Я особенно озадачен тем, что должно быть включено в мою функцию запуска.

Например.

// Setup Bull producers
const Bull = require('bull');
const urgentQueue = new Bull('urgent-queue', REDIS_URL);
const normalQueue = new Bull('normal-queue', REDIS_URL);

// Express app using Slack Bolt framework
const {App, ExpressReceiver} = require('@slack/bolt');
const expressReceiver = new ExpressReceiver({
    signingSecret: process.env.SLACK_SIGNING_SECRET,
    endpoints: '/slack/events'
});

const app = new App({
    authorize: helpers.authorize,
    receiver: expressReceiver
});

// Other functions go here, redacted for example


// Schedule our repeating Bull jobs in our master process
const startMaster = async() => {
    await normalQueue.add('update-presence', {repeat: {cron: '0,15,30,45 * * * *'}});
    await urgentQueue.add('nightly-billing', {repeat: {cron: '0 0 * * *'}});
    await urgentQueue.add('process-trials', {repeat: {cron: '30 0 * * *'}});
    console.log('⚡️ Master process has run!');
};

// Our workers can spawn multiple apps
// QQQ should all of this go inside of the start function (including the express definitions above?)
const startWorker = async () => {
    await app.start(process.env.PORT || 5000);
    console.log('⚡️ Visual Office  app is running!');
};

// Launch with concurrency support
const throng = require('throng');
var WORKERS = process.env.WEB_CONCURRENCY || 1;
throng({
    workers: WORKERS,
    lifetime: Infinity,
    master: startMaster,
    start: startWorker
});
console.log(`launched with ${WORKERS} concurrent processes`)

Мой вопрос сводится к переменной области видимости. В приведенном выше примере кода он ссылается на константы, которые определены вне функций master или start. Большинство примеров, которые я видел, показывают полные express приложения внутри функции запуска, а не просто вызывают функцию app.start внутри функции запуска работника.

Это потому, что обращение к нему таким образом наносит ущерб цели? Как в процессе рабочие ссылаются на один и тот же объект в памяти, несмотря на то, что находятся в отдельных процессах?

1 Ответ

1 голос
/ 25 апреля 2020

В вашем примере показано использование модуля throng. Глядя на его код, он использует модуль nodejs cluster. Я могу предложить некоторые объяснения как в терминах встроенного nodejs cluster модуля (который должен применяться к thong), так и в терминах или встроенного worker_threads модуля.

кластерный модуль

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

модуль worker_thread

Модуль worker_thread позволяет запустить один или несколько потоков в одном процессе. Каждый поток является собственным интерпретатором V8, и они по умолчанию не делят какие-либо переменные или память (глобальную или модульную). Переменные, которые вы передаете между основным потоком и рабочим потоком, либо через workerData, либо через postMessage(), копируются в отдельные переменные в принимающем потоке. Объекты копируются с помощью структурированного процесса клонирования.

Но они находятся в одном и том же процессе, поэтому выполнение вызова, подобного вызову process.exit() из рабочего потока, завершит весь процесс (вы можете отдельно убить только этот поток, если вы хочу).

Рабочие потоки могут совместно использовать память, выделяя память как SharedArrayBuffer, к которой затем могут обращаться все основные потоки и все рабочие. Автоматическая c синхронизация доступа к этой памяти отсутствует, поэтому могут возникнуть ожидаемые проблемы с многопоточным параллелизмом, если только вы не используете некоторые из примитивов синхронизации, которые теперь предлагает nodejs, или если у вас нет такого дизайна, что только один поток в время когда-либо ссылается на эту разделяемую память (это то, что я делал в моем недавнем приложении - передаю sharedArrayBuffer указанному c работнику для выполнения операции, а затем передает его обратно, когда выполнено с заданием, и основной поток не сохраняет ссылку на него, пока рабочий над ним работает).

Распределенная таким образом память не является набором переменных, это буфер памяти, но вы можете интерпретировать память так, как вы хотите, чтобы она превратилась в определенные c значимые переменные.

Обратите внимание, что разделяемая память автоматически не доступна каждому потоку, но вместо этого вы должны передать ее потоку, либо в начальном workerData, либо с помощью postMessage(), прежде чем он получит ссылку на него. sharedMemory буквально выделяется из другой кучи. Я считаю, что он был впервые разработан V8 для использования с webWorkers в браузере, но теперь работает и в node.js.

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