Portanto, tenho uma compreensão de como o Node.js funciona: ele tem um único thread de ouvinte que recebe um evento e o delega a um pool de trabalho. O thread de trabalho notifica o ouvinte assim que conclui o trabalho e, então, o ouvinte retorna a resposta ao chamador.
Minha pergunta é a seguinte: se eu abrir um servidor HTTP em Node.js e chamar o sleep em um dos meus eventos de caminho roteado (como "/ test / sleep"), todo o sistema irá parar. Até mesmo o segmento de ouvinte único. Mas meu entendimento é que esse código está acontecendo no pool de trabalhadores.
Agora, por outro lado, quando eu uso o Mongoose para conversar com o MongoDB, as leituras do banco de dados são uma operação de E / S cara. O Node parece ser capaz de delegar o trabalho a um encadeamento e receber o retorno de chamada quando ele for concluído; o tempo que leva para carregar do banco de dados não parece bloquear o sistema.
Como o Node.js decide usar um thread do pool de threads em vez do thread do listener? Por que não consigo escrever código de evento que dorme e apenas bloqueia um thread do pool de threads?
Respostas:
Seu entendimento de como o nó funciona não está correto ... mas é um equívoco comum, porque a realidade da situação é na verdade bastante complexa e normalmente resumida em pequenas frases incisivas como "o nó é de thread único" que simplificam as coisas .
Por enquanto, ignoraremos o multiprocessamento / multi-threading explícito por meio de cluster e webworker-threads , e apenas falaremos sobre o nó não-threaded típico.
O Node é executado em um único loop de evento. É um único tópico e você só consegue aquele único tópico. Todo o javascript que você escreve é executado neste loop e, se uma operação de bloqueio acontecer nesse código, bloqueará todo o loop e nada mais acontecerá até que termine. Esta é a natureza tipicamente de um único nó sobre o qual você tanto ouve falar. Mas, não é toda a imagem.
Determinadas funções e módulos, geralmente escritos em C / C ++, suportam E / S assíncronas. Quando você chama essas funções e métodos, eles gerenciam internamente a passagem da chamada para um thread de trabalho. Por exemplo, quando você usa o
fs
módulo para solicitar um arquivo, ofs
módulo passa essa chamada para um thread de trabalho, e esse trabalhador espera por sua resposta, que então apresenta de volta para o loop de evento que está ocorrendo sem ele no entretanto. Tudo isso é abstraído de você, o desenvolvedor do nó, e parte dele é abstraído dos desenvolvedores do módulo através do uso do libuv .Conforme apontado por Denis Dollfus nos comentários ( desta resposta a uma pergunta semelhante), a estratégia usada por libuv para alcançar E / S assíncronas nem sempre é um pool de threads, especificamente no caso do
http
módulo, uma estratégia diferente parece ser usado neste momento. Para nossos propósitos aqui, é principalmente importante observar como o contexto assíncrono é alcançado (usando libuv) e que o pool de threads mantido por libuv é uma das várias estratégias oferecidas por essa biblioteca para atingir a assincronicidade.Em uma tangente principalmente relacionada, há uma análise muito mais profunda de como o nó atinge a assincronicidade e alguns problemas potenciais relacionados e como lidar com eles, neste excelente artigo . A maior parte expande o que escrevi acima, mas, adicionalmente, aponta:
UV_THREADPOOL_SIZE
meio da variável de ambiente, desde que faça isso antes que o pool de threads seja necessário e criado:process.env.UV_THREADPOOL_SIZE = 10;
Se você quiser o multiprocessamento tradicional ou multi-threading no nó, você pode obtê-lo através do
cluster
módulo embutido ou vários outros módulos, como o mencionado acimawebworker-threads
, ou você pode fingir implementando alguma forma de fragmentar seu trabalho e manualmente usandosetTimeout
ousetImmediate
ouprocess.nextTick
para pausar seu trabalho e continuá-lo em um loop posterior para permitir que outros processos sejam concluídos (mas isso não é recomendado).Observe que, se você está escrevendo um código de longa execução / bloqueio em javascript, provavelmente está cometendo um erro. Outros idiomas terão um desempenho muito mais eficiente.
fonte
Isso não é realmente preciso. O Node.js tem apenas um único thread de "trabalho" que executa a execução de javascript. Existem threads dentro do nó que tratam do processamento IO, mas pensar neles como "trabalhadores" é um equívoco. Na verdade, existem apenas manipulação de IO e alguns outros detalhes da implementação interna do nó, mas, como um programador, você não pode influenciar seu comportamento a não ser alguns parâmetros diversos, como MAX_LISTENERS.
Não há mecanismo de suspensão em JavaScript. Poderíamos discutir isso mais concretamente se você postasse um trecho de código do que você acha que significa "dormir". Não existe essa função a ser chamada para simular algo como
time.sleep(30)
em python, por exemplo. HásetTimeout
, mas que é fundamentalmente não dorme.setTimeout
e liberesetInterval
explicitamente , não bloqueie, o loop de eventos para que outros bits de código possam ser executados no thread de execução principal. A única coisa que você pode fazer é um loop ocupado da CPU com computação na memória, o que de fato deixará o thread de execução principal sem resposta e deixará o programa sem resposta.O IO da rede é sempre assíncrono. Fim da história. O Disk IO tem APIs síncronas e assíncronas, portanto, não há "decisão". node.js se comportará de acordo com as funções principais da API que você chama de sincronização vs assíncrona normal. Por exemplo:
fs.readFile
vsfs.readFileSync
. Para processos filho, também existem APIschild_process.exec
e separadaschild_process.execSync
.A regra é sempre usar APIs assíncronas. Os motivos válidos para usar as APIs de sincronização são para o código de inicialização em um serviço de rede antes de escutar conexões ou em scripts simples que não aceitam solicitações de rede para ferramentas de construção e esse tipo de coisa.
fonte
fs
, até onde eu seiPool de threads como quando e quem usou:
Em primeiro lugar, quando usamos / instalamos o Node em um computador, ele inicia um processo entre outros processos que é chamado de processo de nó no computador, e continua em execução até que você o mate. E este processo em execução é nosso chamado single thread.
Portanto, o mecanismo de thread único facilita o bloqueio de um aplicativo de nó, mas esse é um dos recursos exclusivos que o Node.js traz para a mesa. Portanto, novamente se você executar seu aplicativo de nó, ele será executado em apenas um único thread. Não importa se você tem 1 ou milhão de usuários acessando seu aplicativo ao mesmo tempo.
Portanto, vamos entender exatamente o que acontece no único thread de nodejs quando você inicia seu aplicativo de nó. Inicialmente o programa é inicializado, então todo o código de nível superior é executado, o que significa que todos os códigos que não estão dentro de nenhuma função de retorno de chamada ( lembre-se de que todos os códigos dentro de todas as funções de retorno de chamada serão executados no loop de evento ).
Depois disso, todos os códigos dos módulos executados registram todo o retorno de chamada, por fim, o loop de eventos iniciado para sua aplicação.
Como discutimos antes, todas as funções de retorno de chamada e códigos dentro dessas funções serão executados no loop de eventos. No loop de eventos, as cargas são distribuídas em diferentes fases. De qualquer forma, não vou discutir sobre loop de eventos aqui.
Bem, para uma melhor compreensão do pool de Threads, estou pedindo que você imagine que, no loop de eventos, os códigos dentro de uma função de retorno de chamada são executados após a conclusão da execução de códigos dentro de outra função de retorno de chamada, agora se houver algumas tarefas, são realmente muito pesadas. Eles então bloqueariam nosso thread único nodejs. E é aí que entra o pool de threads, que é exatamente como o loop de evento, fornecido ao Node.js pela biblioteca libuv.
Portanto, o pool de threads não faz parte do nodejs em si, é fornecido pelo libuv para descarregar tarefas pesadas para o libuv, e o libuv executará esses códigos em suas próprias threads e, após a execução, o libuv retornará os resultados para o evento no loop de eventos.
O pool de threads nos dá quatro threads adicionais, que são completamente separados do thread único principal. E podemos realmente configurá-lo para até 128 threads.
Portanto, todos esses threads juntos formaram um pool de threads. e o loop de eventos pode então descarregar automaticamente tarefas pesadas para o pool de threads.
A parte divertida é que tudo isso acontece automaticamente nos bastidores. Não somos nós, desenvolvedores, que decidimos o que vai e o que não vai para o pool de threads.
Existem muitas tarefas que vão para o pool de threads, como
fonte
Este mal-entendido é apenas a diferença entre multitarefa preventiva e multitarefa cooperativa ...
O sono desliga todo o carnaval porque há realmente uma fila para todas as atrações e você fechou o portão. Pense nisso como "um interpretador JS e algumas outras coisas" e ignore os threads ... para você, há apenas um thread, ...
... então não bloqueie.
fonte