Eu tenho lido pessoalmente o código fonte do node.js & v8.
Entrei em um problema semelhante a você quando tentei entender a arquitetura node.js. para escrever módulos nativos.
O que estou postando aqui é o meu entendimento do node.js e isso também pode ser um pouco fora de controle.
Libev é o loop de eventos que realmente é executado internamente no node.js para executar operações simples do loop de eventos. Foi escrito originalmente para sistemas * nix. A Libev fornece um loop de eventos simples, porém otimizado, para a execução do processo. Você pode ler mais sobre a libev aqui .
O LibEio é uma biblioteca para executar a saída de entrada de forma assíncrona. Ele lida com descritores de arquivo, manipuladores de dados, soquetes etc. Você pode ler mais sobre isso aqui aqui .
O LibUv é uma camada de abstração na parte superior do libeio, libev, c-ares (para DNS) e iocp (para windows assíncrono-io). O LibUv realiza, mantém e gerencia todos os io e eventos no pool de eventos. (no caso de libeio threadpool). Você deve conferir o tutorial de Ryan Dahl no libUv. Isso começará a fazer mais sentido para você sobre como a libUv funciona e você entenderá como o node.js funciona na parte superior do libuv e da v8.
Para entender apenas o loop de eventos javascript, você deve assistir a esses vídeos
Para ver como o libeio é usado com o node.js para criar módulos assíncronos, você deve ver este exemplo .
Basicamente, o que acontece dentro do node.js é que o loop da v8 executa e manipula todas as partes do javascript, bem como os módulos C ++ [quando eles estão executando em um encadeamento principal (conforme a documentação oficial, o node.js é o único encadeamento)]. Quando fora do encadeamento principal, libev e libeio o tratam no conjunto de encadeamentos e libev fornece a interação com o loop principal. Então, pelo que entendi, o node.js tem um loop de eventos permanente: esse é o loop de eventos da v8. Para lidar com tarefas assíncronas em C ++, ele usa um pool de threads [via libeio & libev].
Por exemplo:
eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);
O que aparece em todos os módulos geralmente chama a função Task
no pool de threads. Quando concluído, ele chama a AfterTask
função no thread principal. Considerando que Eio_REQUEST
é o manipulador de solicitações que pode ser uma estrutura / objeto cujo motivo é fornecer comunicação entre o conjunto de encadeamentos e o encadeamento principal.
process.nextTick
- No próximo loop ao redor do loop de eventos, chame esse retorno de chamada. Este não é um alias simples para setTimeout (fn, 0), é muito mais eficiente. A qual loop de evento isso se refere? Loop de eventos V8?Parece que algumas das entidades discutidas (por exemplo: libev etc.) perderam relevância, devido ao fato de que já faz um tempo, mas acho que a pergunta ainda tem um grande potencial.
Deixe-me tentar explicar o funcionamento do modelo orientado a eventos com a ajuda de um exemplo abstrato, em um ambiente UNIX abstrato, no contexto do Node, a partir de hoje.
Perspectiva do programa:
O mecanismo de eventos acima é chamado de estrutura de loop de eventos libuv AKA. O nó utiliza essa biblioteca para implementar seu modelo de programação orientado a eventos.
Perspectiva do nó:
Embora a maioria das funcionalidades seja atendida dessa maneira, algumas (versões assíncronas) das operações de arquivo são realizadas com a ajuda de threads adicionais, bem integrados ao libuv. Enquanto as operações de E / S de rede podem esperar na expectativa de um evento externo, como o outro terminal respondendo com dados etc., as operações de arquivo precisam de algum trabalho do próprio nó. Por exemplo, se você abrir um arquivo e aguardar que o fd esteja pronto com os dados, isso não acontecerá, pois ninguém está lendo realmente! Ao mesmo tempo, se você ler o arquivo embutido no encadeamento principal, ele poderá bloquear outras atividades no programa e causar problemas visíveis, pois as operações do arquivo são muito lentas se comparadas às atividades vinculadas à CPU. Portanto, os encadeamentos internos do trabalhador (configuráveis através da variável de ambiente UV_THREADPOOL_SIZE) são empregados para operar em arquivos,
Espero que isto ajude.
fonte
Uma introdução ao libuv
Também uma foto que descreve o loop de eventos no Node.js por @ BusyRich
Atualização 05/09/2017
De acordo com este loop de eventos do doc Node.js ,
O diagrama a seguir mostra uma visão geral simplificada da ordem de operações do loop de eventos.
nota: cada caixa será chamada de "fase" do loop de eventos.
Visão geral das fases
setTimeout()
esetInterval()
.setImmediate()
.setImmediate()
retornos de chamada são chamados aqui.socket.on('close', ...)
.Entre cada execução do loop de eventos, o Node.js verifica se está aguardando alguma E / S ou temporizadores assíncronos e é encerrado corretamente, se não houver.
fonte
In the node-v0.9.0 version of libuv libev was removed
", mas não há descrição sobre isso no nodejschangelog
. github.com/nodejs/node/blob/master/CHANGELOG.md . E se a libev for removida, agora como a E / S assíncrona está sendo executada no nodejs?Há um loop de eventos na arquitetura do NodeJs.
Modelo de loop de eventos do Node.js.
Os aplicativos de nó são executados em um modelo controlado por evento de thread único. No entanto, o Nó implementa um conjunto de encadeamentos em segundo plano para que o trabalho possa ser executado.
O Node.js adiciona trabalho a uma fila de eventos e, em seguida, tem um único encadeamento executando um loop de eventos. O loop de eventos pega o item superior na fila de eventos, executa e, em seguida, pega o próximo item.
Ao executar um código com vida útil mais longa ou bloqueio de E / S, em vez de chamar a função diretamente, ele adiciona a função à fila de eventos, juntamente com um retorno de chamada que será executado após a conclusão da função. Quando todos os eventos na fila de eventos do Node.js. foram executados, o aplicativo Node.js. é encerrado.
O loop de eventos começa a encontrar problemas quando as funções de nosso aplicativo são bloqueadas na E / S.
O Node.js usa retornos de chamada de evento para evitar ter que esperar pelo bloqueio de E / S. Portanto, todas as solicitações que executam E / S de bloqueio são executadas em um encadeamento diferente em segundo plano.
Quando um evento que bloqueia E / S é recuperado da fila de eventos, o Node.js recupera um encadeamento do conjunto de encadeamentos e executa a função lá, em vez de no encadeamento do loop de eventos principal. Isso evita que a E / S de bloqueio retenha o restante dos eventos na fila de eventos.
fonte
Há apenas um loop de eventos fornecido pelo libuv, o V8 é apenas um mecanismo de tempo de execução JS.
fonte
Como iniciante em javascript, eu também tinha a mesma dúvida: o NodeJS contém 2 loops de eventos? Após uma longa pesquisa e discussão com um dos colaboradores da V8, obtive os seguintes conceitos.
fonte
A
pbkdf2
função possui a implementação JavaScript, mas na verdade delega todo o trabalho a ser feito no lado do C ++.recurso: https://github.com/nodejs/node/blob/master/src/node_crypto.cc
O módulo Libuv tem outra responsabilidade que é relevante para algumas funções muito particulares na biblioteca padrão.
Para algumas chamadas de função de biblioteca padrão, o lado do Node C ++ e o Libuv decidem fazer cálculos caros fora do loop de eventos completamente.
Em vez disso, eles usam algo chamado pool de threads, o pool de threads é uma série de quatro threads que podem ser usados para executar tarefas computacionalmente caras, como a
pbkdf2
função.Por padrão, o Libuv cria 4 threads neste pool de threads.
Além dos encadeamentos usados no loop de eventos, existem outros quatro encadeamentos que podem ser usados para descarregar cálculos dispendiosos que precisam ocorrer dentro de nosso aplicativo.
Muitas das funções incluídas na biblioteca padrão do Nó usam automaticamente esse conjunto de encadeamentos. A
pbkdf2
função é uma delas.A presença desse conjunto de encadeamentos é muito significativa.
Portanto, o Node não é verdadeiramente único, porque existem outros segmentos que o Node usa para realizar algumas tarefas caras em termos de computação.
Se o pool de eventos fosse responsável por executar a tarefa computacionalmente cara, nosso aplicativo Node não poderia fazer mais nada.
Nossa CPU executa todas as instruções dentro de um thread, uma por uma.
Usando o pool de threads, podemos fazer outras coisas dentro de um loop de eventos enquanto os cálculos estão ocorrendo.
fonte