Por que diabos eu usaria o Node.js? Um tutorial caso a caso
Publicados: 2022-03-11Introdução
A crescente popularidade do JavaScript trouxe muitas mudanças, e a face do desenvolvimento web hoje é dramaticamente diferente. As coisas que podemos fazer na web hoje em dia com JavaScript rodando no servidor, bem como no navegador, eram difíceis de imaginar há apenas alguns anos, ou eram encapsuladas em ambientes sandbox como Flash ou Java Applets.
Antes de explorar as soluções Node.js, você pode querer ler sobre os benefícios de usar JavaScript em toda a pilha que unifica a linguagem e o formato de dados (JSON), permitindo que você reutilize os recursos do desenvolvedor de maneira otimizada. Como isso é mais um benefício do JavaScript do que do Node.js especificamente, não o discutiremos muito aqui. Mas é uma vantagem importante incorporar o Node em sua pilha.
Como afirma a Wikipedia: “Node.js é uma compilação empacotada do mecanismo JavaScript V8 do Google, a camada de abstração da plataforma libuv e uma biblioteca principal, que é escrita principalmente em JavaScript”. Além disso, vale destacar que Ryan Dahl, o criador do Node.js, tinha como objetivo criar sites em tempo real com capacidade de push , “inspirados em aplicativos como o Gmail”. No Node.js, ele deu aos desenvolvedores uma ferramenta para trabalhar no paradigma de E/S não bloqueante e orientado a eventos.
Em uma frase: Node.js brilha em aplicações web em tempo real que empregam tecnologia push sobre websockets. O que há de revolucionário nisso? Bem, depois de mais de 20 anos de stateless-web baseado no paradigma stateless request-response, finalmente temos aplicações web com conexões bidirecionais em tempo real, onde tanto o cliente quanto o servidor podem iniciar a comunicação, permitindo que eles troquem dados livremente . Isso contrasta fortemente com o paradigma típico de resposta da Web, em que o cliente sempre inicia a comunicação. Além disso, tudo é baseado na pilha da web aberta (HTML, CSS e JS) rodando na porta padrão 80.
Pode-se argumentar que temos isso há anos na forma de Flash e Java Applets - mas, na realidade, esses eram apenas ambientes de sandbox usando a Web como um protocolo de transporte a ser entregue ao cliente. Além disso, eles foram executados isoladamente e muitas vezes operados em portas não padrão, que podem exigir permissões extras e tal.
Com todas as suas vantagens, o Node.js agora desempenha um papel crítico na pilha de tecnologia de muitas empresas de alto perfil que dependem de seus benefícios exclusivos. A Node.js Foundation consolidou todas as melhores ideias sobre por que as empresas devem considerar o Node.js em uma breve apresentação que pode ser encontrada na página de estudos de caso da Node.js Foundation.
Neste guia do Node.js, discutirei não apenas como essas vantagens são alcançadas, mas também por que você pode querer usar o Node.js — e por que não — usando alguns dos modelos clássicos de aplicativos da Web como exemplos.
Como funciona?
A ideia principal do Node.js: usar E/S não bloqueante e orientada a eventos para permanecer leve e eficiente diante de aplicativos em tempo real com uso intenso de dados que são executados em dispositivos distribuídos.
Isso é um bocado.
O que isso realmente significa é que o Node.js não é uma nova plataforma que dominará o mundo do desenvolvimento web. Em vez disso, é uma plataforma que atende a uma necessidade específica . E entender isso é absolutamente essencial. Você definitivamente não quer usar o Node.js para operações com uso intensivo de CPU; na verdade, usá-lo para computação pesada anulará quase todas as suas vantagens. Onde o Node realmente brilha é na construção de aplicativos de rede rápidos e escaláveis, pois é capaz de lidar com um grande número de conexões simultâneas com alto rendimento, o que equivale a alta escalabilidade.
Como funciona sob o capô é bastante interessante. Em comparação com as técnicas tradicionais de serviço da Web, em que cada conexão (solicitação) gera um novo thread, ocupando a RAM do sistema e, eventualmente, maximizando a quantidade de RAM disponível, o Node.js opera em um único thread, usando I/ sem bloqueio O chamadas, permitindo que ele suporte dezenas de milhares de conexões simultâneas mantidas no loop de eventos.
Um cálculo rápido: supondo que cada thread tenha potencialmente 2 MB de memória, rodando em um sistema com 8 GB de RAM nos coloca em um máximo teórico de 4.000 conexões simultâneas (cálculos retirados do artigo de Michael Abernethy “Just what is Node .js?”, publicado no IBM developerWorks em 2011; infelizmente, o artigo não está mais disponível) , mais o custo da alternância de contexto entre os encadeamentos. Esse é o cenário com o qual você normalmente lida nas técnicas tradicionais de serviço da Web. Ao evitar tudo isso, o Node.js atinge níveis de escalabilidade de mais de 1 milhão de conexões simultâneas e mais de 600 mil conexões de websockets simultâneas.
Há, é claro, a questão de compartilhar um único encadeamento entre todas as solicitações dos clientes, e é uma armadilha potencial de escrever aplicativos Node.js. Em primeiro lugar, a computação pesada poderia sufocar o único thread do Node e causar problemas para todos os clientes (mais sobre isso depois), pois as solicitações recebidas seriam bloqueadas até que a computação fosse concluída. Em segundo lugar, os desenvolvedores precisam ser muito cuidadosos para não permitir que uma exceção borbulhe até o loop de eventos Node.js principal (superior), o que fará com que a instância do Node.js termine (efetivamente travando o programa).
A técnica usada para evitar exceções borbulhando até a superfície é passar erros de volta para o chamador como parâmetros de retorno de chamada (em vez de lançá-los, como em outros ambientes). Mesmo que alguma exceção não tratada consiga surgir, ferramentas foram desenvolvidas para monitorar o processo Node.js e realizar a recuperação necessária de uma instância travada (embora você provavelmente não consiga recuperar o estado atual da sessão do usuário), o mais comum sendo o módulo Forever, ou usando uma abordagem diferente com ferramentas de sistema externas upstart e monit , ou mesmo apenas upstart.
NPM: o gerenciador de pacotes de nós
Ao discutir o Node.js, uma coisa que definitivamente não deve ser omitida é o suporte interno para gerenciamento de pacotes usando NPM, uma ferramenta que vem por padrão com todas as instalações do Node.js. A ideia dos módulos NPM é bastante semelhante à do Ruby Gems : um conjunto de componentes reutilizáveis publicamente disponíveis, disponíveis através de fácil instalação através de um repositório online, com gerenciamento de versões e dependências.
Uma lista completa de módulos empacotados pode ser encontrada no site do npm ou acessada usando a ferramenta npm CLI que é instalada automaticamente com o Node.js. O ecossistema de módulos é aberto a todos, e qualquer pessoa pode publicar seu próprio módulo que será listado no repositório npm.
Alguns dos módulos npm mais úteis hoje são:
- express - Express.js — ou simplesmente Express — uma estrutura de desenvolvimento da Web inspirada em Sinatra para Node.js e o padrão de fato para a maioria dos aplicativos Node.js existentes hoje.
- hapi - uma estrutura centrada na configuração muito modular e simples de usar para criar aplicativos da Web e de serviços
- connect - Connect é uma estrutura de servidor HTTP extensível para Node.js, fornecendo uma coleção de “plugins” de alto desempenho conhecidos como middleware; serve como base para o Express.
- socket.io e sockjs - componente do lado do servidor dos dois componentes de websockets mais comuns disponíveis hoje.
- pug (anteriormente Jade ) - Um dos mecanismos de modelagem populares, inspirado no HAML, um padrão no Express.js.
- mongodb e mongojs - wrappers MongoDB para fornecer a API para bancos de dados de objetos MongoDB em Node.js.
- redis - biblioteca cliente Redis.
- lodash (sublinhado, lazy.js) - O cinto de utilitários JavaScript. Underscore iniciou o jogo, mas foi derrubado por uma de suas duas contrapartes, principalmente devido ao melhor desempenho e implementação modular.
- forever - Provavelmente o utilitário mais comum para garantir que um determinado script de nó seja executado continuamente. Mantém seu processo Node.js em produção em caso de falhas inesperadas.
- bluebird - Uma implementação completa de Promises/A+ com desempenho excepcionalmente bom
- moment - Uma biblioteca de datas JavaScript para analisar, validar, manipular e formatar datas.
A lista continua. Existem toneladas de pacotes realmente úteis por aí, disponíveis para todos (sem ofensas aos que omiti aqui).
Exemplos de onde o Node.js deve ser usado
BATER PAPO
O bate-papo é o aplicativo multiusuário em tempo real mais típico. Desde o IRC (no passado), passando por muitos protocolos proprietários e abertos executados em portas não padrão, até a capacidade de implementar tudo hoje em Node.js com websockets rodando na porta padrão 80.
O aplicativo de bate-papo é realmente o exemplo ideal para o Node.js: é um aplicativo leve, de alto tráfego e com uso intenso de dados (mas com baixo processamento/computação) que é executado em dispositivos distribuídos. Também é um ótimo caso de uso para aprendizado, pois é simples, mas abrange a maioria dos paradigmas que você usará em um aplicativo Node.js típico.
Vamos tentar descrever como funciona.
No exemplo mais simples, temos uma única sala de bate-papo em nosso site, onde as pessoas vêm e podem trocar mensagens de um para muitos (na verdade, todos). Por exemplo, digamos que temos três pessoas no site, todas conectadas ao nosso quadro de mensagens.
No lado do servidor, temos um aplicativo Express.js simples que implementa duas coisas:
- Um manipulador
GET /
solicitação que serve a página da Web contendo um quadro de mensagens e um botão 'Enviar' para inicializar a entrada de uma nova mensagem e - Um servidor de websockets que escuta novas mensagens emitidas por clientes de websockets.
No lado do cliente, temos uma página HTML com alguns manipuladores configurados, um para o evento de clique do botão 'Enviar', que pega a mensagem de entrada e a envia pelo websocket, e outro que ouve novas mensagens recebidas no cliente websockets (ou seja, mensagens enviadas por outros usuários, que o servidor agora deseja que o cliente exiba).
Quando um dos clientes publica uma mensagem, eis o que acontece:
- O navegador captura o clique do botão 'Enviar' através de um manipulador JavaScript, pega o valor do campo de entrada (ou seja, o texto da mensagem) e emite uma mensagem websocket usando o cliente websocket conectado ao nosso servidor (inicializado na inicialização da página web).
- O componente do lado do servidor da conexão websocket recebe a mensagem e a encaminha para todos os outros clientes conectados usando o método de transmissão.
- Todos os clientes recebem a nova mensagem como uma mensagem push por meio de um componente websockets do lado do cliente em execução na página da web. Eles então pegam o conteúdo da mensagem e atualizam a página da web no local anexando a nova mensagem ao quadro.
Este é o exemplo mais simples. Para uma solução mais robusta, você pode usar um cache simples baseado no armazenamento Redis. Ou em uma solução ainda mais avançada, uma fila de mensagens para lidar com o roteamento de mensagens para os clientes e um mecanismo de entrega mais robusto que pode cobrir perdas temporárias de conexão ou armazenar mensagens para clientes registrados enquanto estiverem offline. Mas, independentemente das melhorias que você fizer, o Node.js ainda estará operando sob os mesmos princípios básicos: reagir a eventos, lidar com muitas conexões simultâneas e manter a fluidez na experiência do usuário.

API SOBRE UM BD OBJETO
Embora o Node.js realmente brilhe com aplicativos de tempo real, é bastante natural para expor os dados de bancos de dados de objetos (por exemplo, MongoDB). Os dados armazenados em JSON permitem que o Node.js funcione sem a incompatibilidade de impedância e a conversão de dados.
Por exemplo, se você estiver usando Rails, você converteria de JSON para modelos binários e os exporia de volta como JSON sobre o HTTP quando os dados forem consumidos por Backbone.js, Angular.js, etc., ou mesmo jQuery AJAX simples chamadas. Com o Node.js, você pode simplesmente expor seus objetos JSON com uma API REST para o cliente consumir. Além disso, você não precisa se preocupar em converter entre JSON e qualquer outra coisa ao ler ou gravar em seu banco de dados (se estiver usando o MongoDB). Em suma, você pode evitar a necessidade de várias conversões usando um formato de serialização de dados uniforme no cliente, servidor e banco de dados.
ENTRADAS EM FILA
Se você estiver recebendo uma grande quantidade de dados simultâneos, seu banco de dados pode se tornar um gargalo. Conforme descrito acima, o Node.js pode manipular facilmente as próprias conexões simultâneas. Mas como o acesso ao banco de dados é uma operação de bloqueio (neste caso), temos problemas. A solução é reconhecer o comportamento do cliente antes que os dados sejam realmente gravados no banco de dados.
Com essa abordagem, o sistema mantém sua capacidade de resposta sob uma carga pesada, o que é particularmente útil quando o cliente não precisa de confirmação firme de uma gravação de dados bem-sucedida. Exemplos típicos incluem: o registro ou gravação de dados de rastreamento de usuários, processados em lotes e não usados até um momento posterior; bem como operações que não precisam ser refletidas instantaneamente (como atualizar uma contagem de 'curtidas' no Facebook) onde a consistência eventual (tão frequentemente usada no mundo NoSQL) é aceitável.
Os dados são enfileirados por meio de algum tipo de infraestrutura de armazenamento em cache ou de enfileiramento de mensagens – como RabbitMQ ou ZeroMQ – e digeridos por um processo de gravação em lote de banco de dados separado ou serviços de back-end de processamento intensivo de computação, escritos em uma plataforma de melhor desempenho para essas tarefas. Comportamento semelhante pode ser implementado com outras linguagens/frameworks, mas não no mesmo hardware, com a mesma taxa de transferência alta e mantida.
Resumindo: com o Node, você pode empurrar as gravações do banco de dados para o lado e lidar com elas mais tarde, procedendo como se fossem bem-sucedidas.
FLUXO DE DADOS
Em plataformas web mais tradicionais, solicitações e respostas HTTP são tratadas como eventos isolados; na verdade, eles são realmente streams. Essa observação pode ser utilizada no Node.js para criar alguns recursos interessantes. Por exemplo, é possível processar arquivos enquanto eles ainda estão sendo carregados, pois os dados chegam por meio de um fluxo e podemos processá-los online. Isso pode ser feito para codificação de áudio ou vídeo em tempo real e proxy entre diferentes fontes de dados (consulte a próxima seção).
PROCURAÇÃO
O Node.js é facilmente empregado como um proxy do lado do servidor, onde pode lidar com uma grande quantidade de conexões simultâneas de maneira não bloqueante. É especialmente útil para fazer proxy de diferentes serviços com tempos de resposta diferentes ou coletar dados de vários pontos de origem.
Um exemplo: considere um aplicativo do lado do servidor se comunicando com recursos de terceiros, extraindo dados de diferentes fontes ou armazenando ativos como imagens e vídeos em serviços de nuvem de terceiros.
Embora existam servidores proxy dedicados, usar o Node pode ser útil se sua infraestrutura de proxy for inexistente ou se você precisar de uma solução para desenvolvimento local. Com isso, quero dizer que você pode criar um aplicativo do lado do cliente com um servidor de desenvolvimento Node.js para ativos e solicitações de API de proxy/stubbing, enquanto na produção você lida com essas interações com um serviço de proxy dedicado (nginx, HAProxy, etc.). .).
CORRETAGEM - PAINEL DO COMERCIANTE DE AÇÕES
Vamos voltar ao nível do aplicativo. Outro exemplo em que o software de desktop domina, mas pode ser facilmente substituído por uma solução web em tempo real é o software de negociação de corretoras, usado para rastrear preços de ações, realizar cálculos/análises técnicas e criar gráficos/gráficos.
Mudar para uma solução baseada na web em tempo real permitiria aos corretores trocar facilmente de estações de trabalho ou locais de trabalho. Em breve, poderemos começar a vê-los na praia da Flórida... ou Ibiza... ou Bali.
PAINEL DE MONITORAMENTO DE APLICATIVOS
Outro caso de uso comum em que o Node-with-web-sockets se encaixa perfeitamente: rastrear visitantes do site e visualizar suas interações em tempo real.
Você pode coletar estatísticas em tempo real de seu usuário ou até mesmo movê-las para o próximo nível, introduzindo interações direcionadas com seus visitantes, abrindo um canal de comunicação quando eles chegarem a um ponto específico em seu funil. (Se você estiver interessado, essa ideia já está sendo produzida pelo CANDDi.)
Imagine como você poderia melhorar seus negócios se soubesse o que seus visitantes estavam fazendo em tempo real – se pudesse visualizar suas interações. Com os soquetes bidirecionais em tempo real do Node.js, agora você pode.
PAINEL DE MONITORAMENTO DO SISTEMA
Agora, vamos visitar o lado da infraestrutura. Imagine, por exemplo, um provedor de SaaS que queira oferecer a seus usuários uma página de monitoramento de serviços, como a página de status do GitHub. Com o event-loop do Node.js, podemos criar um poderoso painel baseado na web que verifica os status dos serviços de maneira assíncrona e envia dados aos clientes usando websockets.
Tanto os status internos (intra-empresa) quanto os serviços públicos podem ser informados ao vivo e em tempo real usando esta tecnologia. Leve essa ideia um pouco mais longe e tente imaginar aplicativos de monitoramento do Network Operations Center (NOC) em uma operadora de telecomunicações, provedor de nuvem/rede/hospedagem ou alguma instituição financeira, todos executados na pilha da Web aberta apoiada por Node.js e websockets em vez de Java e/ou Java Applets.
Onde o Node.js pode ser usado
APLICATIVOS DA WEB DO LADO DO SERVIDOR
O Node.js com Express.js também pode ser usado para criar aplicativos Web clássicos no lado do servidor. No entanto, embora possível, esse paradigma de solicitação-resposta no qual o Node.js carregaria HTML renderizado não é o caso de uso mais típico. Há argumentos a favor e contra essa abordagem. Aqui estão alguns fatos a serem considerados:
Prós:
- Se seu aplicativo não tiver nenhuma computação intensiva de CPU, você poderá construí-lo em Javascript de cima para baixo, até mesmo no nível do banco de dados, se você usar o banco de objetos de armazenamento JSON como o MongoDB. Isso facilita o desenvolvimento (incluindo a contratação) significativamente.
- Os rastreadores recebem uma resposta HTML totalmente renderizada, que é muito mais amigável para SEO do que, digamos, um aplicativo de página única ou um aplicativo websockets executado no Node.js.
Contras:
- Qualquer computação intensiva da CPU bloqueará a capacidade de resposta do Node.js, portanto, uma plataforma encadeada é uma abordagem melhor. Como alternativa, você pode tentar dimensionar a computação [*].
- Usar o Node.js com um banco de dados relacional ainda é bastante trabalhoso (veja abaixo mais detalhes). Faça um favor a si mesmo e escolha qualquer outro ambiente como Rails, Django ou ASP.Net MVC se estiver tentando realizar operações relacionais.
Onde o Node.js não deve ser usado
APLICATIVO DA WEB DO LADO DO SERVIDOR COM UM BD RELACIONAL ATRÁS
Comparando o Node.js com o Express.js com o Ruby on Rails, por exemplo, costumava haver uma decisão limpa em favor do último quando se tratava de acessar bancos de dados relacionais como PostgreSQL, MySQL e Microsoft SQL Server.
As ferramentas de banco de dados relacional para Node.js ainda estavam em seus estágios iniciais. Por outro lado, o Rails fornece automaticamente configuração de acesso a dados pronta para uso junto com ferramentas de suporte a migrações de esquema de banco de dados e outras Gems (trocadilhos). Rails e suas estruturas de pares têm implementações maduras e comprovadas da camada de acesso a dados do Active Record ou Data Mapper.[*]
Mas as coisas mudaram. Sequelize, TypeORM e Bookshelf percorreram um longo caminho para se tornarem soluções ORM maduras. Também pode valer a pena conferir Join Monster se você deseja gerar SQL a partir de consultas GraphQL.
COMPUTAÇÃO/PROCESSAMENTO DO LADO DO SERVIDOR PESADO
Quando se trata de computação pesada, o Node.js não é a melhor plataforma disponível. Não, você definitivamente não quer construir um servidor de computação Fibonacci em Node.js. Em geral, qualquer operação com uso intensivo de CPU anula todos os benefícios de throughput que o Node oferece com seu modelo de E/S sem bloqueio e orientado a eventos, porque qualquer solicitação de entrada será bloqueada enquanto o encadeamento estiver ocupado com seu processamento de números - supondo que você esteja tentando para executar seus cálculos na mesma instância do Node com a qual você está respondendo às solicitações.
Conforme declarado anteriormente, o Node.js é de thread único e usa apenas um único núcleo de CPU. Quando se trata de adicionar simultaneidade em um servidor multinúcleo, há algum trabalho sendo feito pela equipe do núcleo do Node na forma de um módulo de cluster [ref: http://nodejs.org/api/cluster.html]. Você também pode executar várias instâncias de servidor Node.js facilmente atrás de um proxy reverso via nginx.
Com o clustering, você ainda deve descarregar toda a computação pesada para processos em segundo plano escritos em um ambiente mais apropriado para isso e fazer com que eles se comuniquem por meio de um servidor de fila de mensagens como o RabbitMQ.
Mesmo que seu processamento em segundo plano possa ser executado inicialmente no mesmo servidor, essa abordagem tem potencial para uma escalabilidade muito alta. Esses serviços de processamento em segundo plano podem ser facilmente distribuídos para servidores de trabalho separados sem a necessidade de configurar as cargas de servidores Web frontais.
É claro que você usaria a mesma abordagem em outras plataformas também, mas com o Node.js você obtém a alta taxa de transferência de reqs/s sobre a qual falamos, pois cada solicitação é uma pequena tarefa tratada com muita rapidez e eficiência.
Conclusão
Discutimos o Node.js da teoria à prática, começando com seus objetivos e ambições e terminando com seus pontos positivos e armadilhas. Quando as pessoas têm problemas com o Node, quase sempre se resume ao fato de que as operações de bloqueio são a raiz de todos os males - 99% dos usos indevidos do Node são uma consequência direta.
Lembre-se: o Node.js nunca foi criado para resolver o problema de dimensionamento de computação. Ele foi criado para resolver o problema de dimensionamento de E/S, o que faz muito bem.
Por que usar o Node.js? Se o seu caso de uso não contém operações intensivas de CPU nem acesso a recursos de bloqueio, você pode explorar os benefícios do Node.js e desfrutar de aplicativos de rede rápidos e escaláveis. Bem-vindo à web em tempo real.