Зачем нам нужны кластеры узлов, если мы можем изменить количество потоков, используемых пулом потоков?

#javascript #node.js #multithreading #multiprocessing #libuv

Вопрос:

Название в основном говорит само за себя, зачем нам создавать работников узлов, когда мы можем просто изменить количество потоков, которые использует пул потоков libuv?

Ответ №1:

libuv Пул потоков не используется для вашего кода JavaScript и используется только для подмножества Node.js API (хотя он используется одним из самых крупных fs ). Из документации:

Асинхронные системные API используются Node.js когда это возможно, но там, где их нет, libuv пул потоков используется для создания API асинхронных узлов на основе API синхронной системы. Node.js API, использующие пул потоков, являются:

  • все fs API, кроме API-интерфейсов наблюдателя файлов и тех, которые явно синхронны
  • асинхронные криптографические API, такие как crypto.pbkdf2() , crypto.scrypt() , crypto.randomBytes() , crypto.randomFill() , crypto.generateKeyPair()
  • dns.lookup()
  • все zlib API, кроме явно синхронных

Таким образом, размер libuv пула потоков помогает при большом количестве перекрывающихся fs и похожих вызовов, но это еще не вся история.

libuv Пул не поможет вам, если у вас есть код JavaScript, который должен выполнять значительную работу синхронно; этот код выполняется в одном потоке (если вы не запускаете рабочих). Более того, Node.js использует тот же поток для проверки выполнения асинхронной работы (включая libuv завершение). Со страницы цикла событий:

На следующей диаграмме показан упрощенный обзор порядка операций цикла событий.

    ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘
 

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

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

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

Комментарии:

1. Спасибо! Я думаю, я неправильно понял, как узел справляется с асинхронными операциями, я думал, что все они были выгружены eventloop в пул потоков libuv, но если это не так, где выполняются эти операции (например, извлечение информации из бд)?

2. @ThabetSabha — Они начинают с основного потока, используя асинхронные API в операционной системе, которые позволяют этому потоку выполнять другие задачи во время выполнения работы. Эти асинхронные API ОС предоставляют способ проверки завершения, который поток выполняет в промежутках между вызовами вашего кода JavaScript. Я не специалист по Node.js цикл событий, и я уверен, что в нем больше тонкости, чем в этом, но это общая идея, которую они передают на странице связанного цикла событий.

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