[JavaScript, Программирование, Node.JS] Как управлять несколькими потоками в Node JS (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
22-Июн-2021 01:30


В этом посте я собираюсь показать вам, как потенциально утроить производительность вашего приложения Node за счет управления несколькими потоками. Это важный учебник, в котором показанные методы и примеры дадут вам все необходимое для настройки эффективного управления потоками.Извините, данный ресурс не поддреживается. :( Дочерние процессы, кластеризация и Worker ThreadsВ течение долгого времени Node обладал способностью быть многопоточным, используя дочерние процессы, кластеризацию, или более предпочтительный в последнее время метод модуля под названием Worker Threads.Дочерние процессы были первоначальным средством создания нескольких потоков для вашего приложения и были доступны с версии 0.10. Это достигалось путем создания узлового процесса для каждого дополнительного потока, который вы хотели создать.Кластеризация, которая стала стабильной примерно с версии 4, позволяет нам упростить создание и управление дочерними процессами. Она великолепно работает в сочетании с PM2.Теперь, прежде чем мы перейдем к многопоточности нашего приложения, есть несколько моментов, которые должны быть полностью поняты:1. Многопоточность уже существует для задач ввода/выводаВ Node есть слой, который уже является многопоточным, и это пул потоков libuv. Задачи ввода-вывода, такие как управление файлами и папками, транзакции TCP/UDP, сжатие и шифрование, передаются libuv, и если они не являются асинхронными по своей природе, то обрабатываются в пуле потоков libuv.2. Child Processes (Дочерние процессы)/Worker Threads работают только для синхронной логики JavaScript.Имплементация многопоточности с помощью дочерних процессов или Worker Threads будет эффективна только для синхронного кода JavaScript, выполняющего трудоемкие операции, такие как циклы, вычисления и т.д. Если вы в качестве примера попытаетесь переложить задачи ввода-вывода на Worker Threads, то не увидите улучшения производительности.3. Создать один поток легко. Динамично управлять несколькими потоками сложноСоздать один дополнительный поток в вашем приложении достаточно просто, поскольку существует масса руководств о том, как это сделать. Однако создание потоков, эквивалентных количеству логических ядер вашей или виртуальной машины, и управление распределением работы между этими потоками является более сложной задачей, разработка такой логики достаточно трудозатратна.Хорошо, что мы живем в мире открытого исходного кода и блестящего вклада сообщества Node. Это значит, что уже существует модуль, который даст нам полную возможность динамически создавать и управлять потоками в зависимости от доступности ЦП нашей или виртуальной машины.Worker PoolМодуль, с которым мы будем работать сегодня, называется Worker Pool. Созданный Джосом де Йонгом, Worker Pool предлагает простой способ создания пула воркеров для динамической разгрузки вычислений, а также для управления пулом выделенных воркеров. По сути, это менеджер пула потоков для Node JS, поддерживающий Worker Threads, дочерние процессы и Web Workers для браузерных имплементаций.Чтобы использовать модуль Worker Pool в нашем приложении, необходимо выполнить следующие задачи:
  • Установить Worker Pool
Сначала нам нужно установить модуль Worker Pool - npm install workerpool
  • Инициализировать Worker Pool
Далее нам нужно будет инициализировать Worker Pool при запуске нашего приложения.
  • Создать Middleware Layer
Затем нам нужно будет создать Middleware Layer (промежуточный слой) между нашей сложной JavaScript-логикой и Worker Pool, который будет управлять ею.
  • Обновить существующую логику
Наконец, нам нужно обновить наше приложение, чтобы при необходимости передавать трудоемкие задачи Worker Pool.Управление несколькими потоками с помощью Worker PoolНа данном этапе у вас есть 2 варианта: Использовать свое собственное приложение NodeJS (и установить модули workerpool и bcryptjs), или загрузить исходный код с GitHub по этому руководству и моей серии видео об оптимизации производительности NodeJS.Если вы выберете последний вариант, файлы по данному руководству будут находиться в папке 06-multithreading. После загрузки войдите в корневую папку проекта и запустите npm install. Затем войдите в папку 06-multithreading, чтобы продолжить работу.
В папке worker-pool у нас есть 2 файла: один - это логика контроллера для Worker Pool (controller.js). Другой (файл) содержит функции, которые будут запускаться потоками... он же так называемый промежуточный слой, о котором я говорил ранее (thread-functions.js).worker-pool/controller.js
'use strict'
const WorkerPool = require('workerpool')
const Path = require('path')
let poolProxy = null
// FUNCTIONS
const init = async (options) => {
  const pool = WorkerPool.pool(Path.join(__dirname, './thread-functions.js'), options)
  poolProxy = await pool.proxy()
  console.log(`Worker Threads Enabled - Min Workers: ${pool.minWorkers} - Max Workers: ${pool.maxWorkers} - Worker Type: ${pool.workerType}`)
}
const get = () => {
  return poolProxy
}
// EXPORTS
exports.init = init
exports.get = get
В файле controller.js мы используем модуль workerpool. У нас также есть 2 экспортируемые функции, которые называются init и get. Функция init будет выполняться один раз во время загрузки нашего приложения. Она инстанцирует Worker Pool с опциями, которые мы предоставим, и ссылкой на thread-functions.js. Она также создает прокси, который будет храниться в памяти до тех пор, пока работает наше приложение. Функция get просто возвращает прокси в памяти.worker-pool/thread-functions.js
'use strict'
const WorkerPool = require('workerpool')
const Utilities = require('../2-utilities')
// MIDDLEWARE FUNCTIONS
const bcryptHash = (password) => {
  return Utilities.bcryptHash(password)
}
// CREATE WORKERS
WorkerPool.worker({
  bcryptHash
})
В файле thread-functions.js создадим воркер-функции, которые будут управляться Worker Pool. В нашем примере применим BcryptJS для хэширования паролей. Это обычно занимает около 10 миллисекунд, в зависимости от скорости работы используемой машины, и является хорошим решением для трудоемких задач. Внутри файла utilities.js находится функция и логика, которая хэширует пароль. Все, что мы делаем в функциях потока, заключается в выполнении этого bcryptHash через функцию workerpool. Таким образом, мы сохраняем код централизованным и избегаем дублирования или путаницы в том, где существуют определенные операции.2-utilities.js
'use strict'
const BCrypt = require('bcryptjs')
const bcryptHash = async (password) => {
  return await BCrypt.hash(password, 8)
}
exports.bcryptHash = bcryptHash
.env
NODE_ENV="production"
PORT=6000
WORKER_POOL_ENABLED="1"
Файл .env содержит номер порта и устанавливает переменную NODE_ENV на "production". Здесь же мы указываем, хотим ли мы включить или отключить Worker Pool, устанавливая WORKER_POOL_ENABLED в "1" или "0".1-app.js
'use strict'
require('dotenv').config()
const Express = require('express')
const App = Express()
const HTTP = require('http')
const Utilities = require('./2-utilities')
const WorkerCon = require('./worker-pool/controller')
// Router Setup
App.get('/bcrypt', async (req, res) => {
  const password = 'This is a long password'
  let result = null
  let workerPool = null
  if (process.env.WORKER_POOL_ENABLED === '1') {
    workerPool = WorkerCon.get()
    result = await workerPool.bcryptHash(password)
  } else {
    result = await Utilities.bcryptHash(password)
  }
  res.send(result)
})
// Server Setup
const port = process.env.PORT
const server = HTTP.createServer(App)
;(async () => {
  // Init Worker Pool
  if (process.env.WORKER_POOL_ENABLED === '1') {
    const options = { minWorkers: 'max' }
    await WorkerCon.init(options)
  }
  // Start Server
  server.listen(port, () => {
    console.log('NodeJS Performance Optimizations listening on: ', port)
  })
})()
И последнее, файл 1-app.js содержит код, который будет выполняться при запуске нашего приложения. Сначала инициализируем переменные в файле .env. Затем настроим сервер Express и создадим маршрут под названием /bcrypt. При запуске этого маршрута проверим, включен ли Worker Pool. Если да, то получим управление прокси Worker Pool и выполним функцию bcryptHash, которую мы объявили в файле thread-functions.js. Она, в свою очередь, выполнит функцию bcryptHash в Utilities и вернет нам результат. Если Worker Pool отключен, тогда просто выполним функцию bcryptHash непосредственно в Utilities.В конце нашего файла 1-app.js находится функция, вызывающая саму себя. Это сделано для поддержки async/await, которую мы используем при взаимодействии с Worker Pool. Далее инициализируем Worker Pool, если он включен. Единственная конфигурация, которую надо переопределить - установка minWorkers на "max". Это гарантирует, что Worker Pool породит столько потоков, сколько логических ядер есть на нашей машине, за исключением 1 логического ядра, которое используется для главного потока. В моем случае имеется 6 физических ядер с гиперпоточностью, это означает, что у меня в распоряжении 12 логических ядер. Поэтому при значении minWorkers равном "max", Worker Pool будет создавать и управлять 11 потоками. Наконец, последний фрагмент кода - это запуск нашего сервера и прослушивание порта 6000.Тестирование Worker PoolТестировать Worker Pool очень просто: запустите приложение и во время его работы выполните запрос get на http://localhost:6000/bcrypt. Если у вас есть инструментарий нагрузочного тестирования, такой как AutoCannon, вы можете посмотреть разницу в производительности при включении/выключении Worker Pool. AutoCannon очень прост в использовании.ЗаключениеЯ надеюсь, что это руководство дало вам представление об управлении несколькими потоками в вашем приложении Node. Вложенное видео в начале статьи наглядно демонстрирует процесс тестирования приложения Node.
Перевод подготовлен в рамках курса "Node.js Developer". Если интересно узнать о курсе больше, регистрируйтесь на день открытых дверей.

===========
Источник:
habr.com
===========

===========
Автор оригинала: JOHN JARDIN | Bleeding Code
===========
Похожие новости: Теги для поиска: #_javascript, #_programmirovanie (Программирование), #_node.js, #_javascript, #_node.js, #_programming, #_blog_kompanii_otus (
Блог компании OTUS
)
, #_javascript, #_programmirovanie (
Программирование
)
, #_node.js
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 12:55
Часовой пояс: UTC + 5