-
@ Bolsonitro Ucrânia 🇺🇦🇻🇦
2024-10-07 22:04:01Ao trabalhar para melhorar o desempenho de aplicativos Node.js, é crucial entender como o Node.js e as bibliotecas em uso funcionam debaixo dos panos.
javascript app.post('/endpoint', async (req, res) => { // DO SOMETHING })
Como o Node.js e o Express lidam com essa solicitação até o ponto em que a função é chamada dentro de app.post? Como o loop de eventos do Node.js funciona com promises nos bastidores?
O Node.js não consegue gerenciar tantas conexões paralelas quanto o Elixir. Tanto o engine quanto as bibliotecas têm certas limitações. Por exemplo, se estivermos trabalhando com um microsserviço que atua como middleware — recebendo informações, chamando uma API externa, aguardando a resposta e retornando dados para outro microsserviço — o Node.js precisa manter a conexão TCP ativa enquanto aguarda a resposta do serviço externo. Por isso, o número de solicitações por segundo diminui, e o Node.js consome recursos significativos (CPU e memória) gerenciando conexões abertas e promises não resolvidas enquanto espera que serviços externos respondam, para então resolver as promises que estãp em await, para então responder as solicitações e fechar as conexões TCP.
Uma maneira de melhorar o desempenho em um aplicativo Express é manipular informações em lotes. Em vez de fazer 100 solicitações individuais ao microsserviço, cada uma das quais chama um serviço externo e gerencia 100 conexões TCP abertas e 100 promessas pendentes (teórico), a aplicação pode receber uma única solicitação contendo um array de 100 itens. Usando await Promise.all em um array de 100 promessas, o Node.js precisa gerenciar apenas uma conexão TCP enquanto aguarda os resultados de todas as 100 promessas. Embora ainda trabalhemos com 100 promessas, reduzimos a quantidade de requisições HTTP de 100 para 1, sobrando muito mais recursos para outras atividades.
Usando esse método, onde lambdas AWS passaram à chamar um microserviço passando lotes de itens em vez de fazer várias chamadas HTTP para cada item, o microserviço passou à apresentar um menor consumo de CPU e memória, e deixou de cair o container. Um ponto negativo é que o tempo de resposta para processar um array de 100 itens foi muitas vezes maior que para processar apenas um único item, entretanto, houve um ganho no sistema como um todo (lambdas AWS e microserviços), pois a quantidade de erros 503 por excesso de solicitações passou à ser zero.
Aqui tem um artigo com alguns testes de carga do NodeJs e Express, mostrando como que uma quantidade maior de requisições HTTP leva à um tempo menor de resposta, apesar de não haver nenhuma modificação na promise que era executada: Exploring how Node.js handles HTTP connections