DEV Community

Abel Costa
Abel Costa

Posted on

3

NodeJs, introdução e conceitos centrais

Você nunca pode entender tudo, mas você deve se esforçar para entender o sistema - Ryan Dahl, criador do NodeJs

O que é Nodejs?

Para começar vamos colocar que NodeJs, não é:

  • Uma linguagem
  • Um framework

NodeJs pode ser definido como um ambiente de execução (runtime) de códigos Javascript server-side. Os principais motivos para a sua adoção se dão pela fácil escalabilidade, baixo custo e flexibilidade o que torna o seu uso adequado para a programação de microsserviços e componentes de uma arquitetura serverless.

Seu criador, Ryan Dahl, estava trabalhando numa feature para um sistema que se assemelhava muito com uma barra de progresso, features como essa têm a característica de consumirem muita memória e processamento, isso porque são feitas muitas operações de comunicação com o banco de dados. Naquele momento a grande maioria das plataformas faziam uso de uma abordagem de I/O baseadas em threads, isso significa que operações que precisam esperar por uma resposta, como a query feita ao banco de dados, ficam "presas" dentro de uma thread que tem o seu estado marcado como waiting. Justamente por isso o consumo de memória e processamento da aplicação subia muito, as threads precisam guardar as informações na memória, e o escalonador de processos responsável por fazer a alternância entre as threads consome um bom tempo de processamento.

O principal diferencial do NodeJs para outras tecnologias como PHP, C# e Java é que ele é Single-Thread. Nas outras tecnologias uma thread é criada para cada requisição feita, então se uma dessas threads faz uma operação muito demorada ela vai ficar travada até o final da execução. O modelo Single-Thread é o modelo padrão, mais comum de se encontrar, mais fácil de programar, porém mais oneroso para os recursos computacionais. Já no modelo NodeJs temos apenas uma thread chamada de Event Loop, a princípio as requisições são colocadas na fila de requisições (ou também fila de eventos) e o event loop vai desempilhar cada uma dessas requisições e tratá-las. A máquina virtual do Javascript então verifica o que tem de ser feito, delega a sua execução e volta para atender as outras requisições, quando a atividade delegada termina então ela volta para o fluxo principal para ser devolvida ao requisitante.

Modelo do NodeJs x Modelo Tradicional

A analogia do restaurante

Quando um novo cliente chega num restaurante um garçom vai prontamente atendê-lo, anota o seu pedido e repassa o mesmo para o cozinheiro que começa a prepará-lo então volta para o salão para atender novos clientes que chegam, anota novos pedidos e repassa-os ao cozinheiro, esse ciclo é repetido diversas vezes, então a medida que os pedidos vão ficando prontos eles são entregues aos clientes pelo mesmo garçom. É mais ou menos assim que o Node funciona, podemos ver o garçom como o event loop e o cozinheiro como a worker pool, os clientes são os usuários de uma determinada aplicação, os pratos são os processos de regras de negócio como por exemplo, acessar o banco de dados.

A arquitetura NodeJs

O NodeJs é composto por componentes e cada um deles possui uma função específica, no coração dele temos o V8, uma engine Javascript usada principalmente dentro do navegador Google Chrome, ela vai ser a responsável por executar o código escrito em Javascript, então temos os NodeJs addons, código escrito em C responsável por comunicar (ligar) o V8 com o sistema operacional. Por último, e não menos importante temos a biblioteca libuv, uma biblioteca escrita em C especialmente para o NodeJs que tem como função abstrair operações não bloqueantes de I/O, isso permite que o CPU e outros recursos sejam usados enquanto ainda estão executando operações de entrada e saída.

Arquitetura do NodeJs

As threads dentro do NodeJs

A pesar do NodeJs ser em si Single-Thread ele faz uso de outras threads para a execução das atividades, temos dois tipos básicos de threads dentro do Node, a thread do Event Loop e as Threads do Worker Pool.

Qual código é executado dentro do Event Loop?

Os aplicativos NodeJs passam pela fase de inicialização, onde fazem a solicitação dos módulos e registram os callbacks que devem ser executados quando houver a resposta dos eventos. Dentro do event loop as solicitações feitas pelas requisições são respondidas de forma síncrona. O event loop também é responsável por registrar todas as requisições assíncronas para continuar o processamento após a conclusão. Os retornos das funções assíncronas também são executados dentro do event loop.

Em resumo, o Event Loop executa os callbacks JavaScript registrados para eventos e também é responsável por atender solicitações assíncronas sem bloqueio, como entrada e saída de rede.

Qual código é executado dentro do worker pool?

O worker pool é implementado sobre a libuv, que expõe uma api que permite enviar tarefas, porém as tarefas reservadas ao worker pool são aquelas que são "caras" no sentido de processamento, basicamente toda e qualquer tarefa a qual o sistema operacional não fornece de forma não bloqueante. Exemplo: se um programa está consultando o banco de dados, a CPU fica ociosa até que a consulta seja processada, nesse meio tempo o programa fica parado, causando desperdício de recursos do sistema. Para evitar isso, o libuv é usado no NodeJs, o que facilita operações de I/O sem bloqueio.

Como o node decide que código vai ser executado?

Através de filas, o worker pool tem uma fila que armazenas as tarefas a serem executadas, de forma com que ela pega a primeira tarefa dessa fila, trabalha nela e quando termina lança uma evento "At least one task is finished" para o event loop. Este por outro lado, na verdade, não mantém de fato uma fila, mas sim uma coleção de descritores de arquivos que juntamente com o sistema operacional monitora os eventos. Quando o sistema operacional diz que um determinado descritor de arquivo está pronto, o event loop traduz para o evento apropriado e chama o callback associado com ele.

O significado para o design de aplicações

Em sistemas preemptivos que alocam uma thread por cliente como o apache, sempre que uma das threads fica em estado de bloqueado por muito tempo o sistema vai interromper a mesma e passar a vez para outra thread, outro cliente. Mas o Node é não preemptivo, isso significa que nenhuma thread é interrompida. Isso diminui muito o consumo de memória e processamento, porque diminui a incidência de troca de contexto na aplicação.

Comparativo entre apache e nginx

Em sistemas preemptivos, onde temos um thread por cliente temos de fazer a troca de contexto com uma certa frequência, assim temos de usar muito mais memória se comparado a um sistema não preemptivo. Na imagem acima temos o apache e o nginx, esse último foi de onde o conceito de event loop foi retirado por Ryan Dahl, note que o Apache, preemptivo, que aloca uma thread por requisição, consome muito mais memória que o nginx que possui processamento paralelo.

O processamento paralelo

A libuv é uma biblioteca cross-platform que permite I/O assíncrono, ela foi desenhada primeiramente para lidar com o Nodejs, mas também é usada em outros ambientes como Julia, Luvit e uvloop. Dentre suas principais responsabilidades temos:

  • Um event loop que é uportado em qualquer plataforma
  • Disponibilização de sockets TCP e UDP de forma assíncrona
  • Disponibilização de eventos do file system
  • Controle de child processes
  • Thread pool
  • Signal handling

Bibliografia:

https://www.youtube.com/watch?v=nfrVPzDJZQc

https://stackoverflow.com/questions/36766696/which-is-correct-node-js-architecture

https://www.geeksforgeeks.org/libuv-in-node-js/

https://nodejs.org/en/docs/guides/dont-block-the-event-loop/

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs