Node.js para desenvolvedores de PHP: 5 aspectos pragmáticos imperdíveis com exemplos de código

Embora a popularidade do Node.js esteja a aumentar, a tracção do PHP está a diminuir. Com esse contexto, este post vai desenvolver em 5 aspectos práticos imperdíveis do uso do Node.js para desenvolvedores de PHP. Estas serão coisas sobre as quais ninguém fala ou escreve, hora de começar.

  1. Node.js para desenvolvedores PHP (não Node.js vs PHP)
  2. Node.js para desenvolvedores PHP o lado prático
    1. Node.js execução de código é assimétrica e não seqüencial
      1. Node.js promete possibilidades
    2. Processo Node.js é de longa duração, ao contrário do PHP
      1. Exemplo de memorização
      2. Exemplo de Pool de conexão com MySQL
    3. Debugging é mais fácil no Node.js do que em PHP
    4. Atualizações maiores no Node.js é mais fácil em PHP
    5. Dockerizar uma aplicação Node.js é uma brisa em comparação com PHP
  3. Conclusão

Nó.js para desenvolvedores PHP (não Node.js vs PHP) #

Esta peça é uma lista de coisas que você como desenvolvedor PHP deve saber e aprender a usar o Node.js efetivamente. Pelo contrário, este post não é um Node.js vs PHP escreva onde o PHP é batido. Eu já usei ambas as linguagens. Eu comecei a escrever mais Node.js em 2016. Quando comecei, enfrentei algumas dificuldades, pois estava acostumado com PHP no trabalho há mais de 7 anos antes disso. Houve um livro lançado no final de 2012 cobrindo Node.js para desenvolvedores PHP.

Este post no blog não vai falar sobre o que é PHP ou Node.js, você pode ler sobre ele em outros posts. Eu também não vou falar muito sobre o Non-Blocking I/O ou sobre o loop do evento. Ainda assim, algumas delas serão folheadas ao discutir os aspectos práticos de escrever um bom código Node.js.

Node.js para desenvolvedores de PHP o lado prático #

PHP está vivo desde 1995 e, segundo consta, ainda é usado por 79,% dos sites monitorados pela W3tech (não posso dizer se é toda a internet). Então as chances são muito altas de que você tenha usado PHP ou implantado algo escrito em PHP. Por exemplo, com uma tendência crescente:

WordPress é usado por 63,7% de todos os sites cujo sistema de gerenciamento de conteúdo conhecemos. Isto é 39,0% de todos os websites monitorados pela W3Tech.

Por outro lado, o Node.js foi lançado em 2009. Grandes empresas de tecnologia como Linked In e Paypal começaram a adotá-lo até 2011 a 2013 por várias razões como microserviços. Como por Stack Overflow pesquisa de desenvolvedores de 2020:

Pelo segundo ano consecutivo, Node.js ocupa o primeiro lugar, pois é usado pela metade dos entrevistados.

Não é segredo que Node.js está ficando muito popular nos últimos 5 anos.

Então como um desenvolvedor PHP, estas são 5 coisas práticas imperdíveis para ser um grande engenheiro de software Node.js. Node.js para desenvolvedores PHP é similar em algum sentido mas também diferente em alguns outros aspectos alguns são descritos abaixo:

Node.js execução de código é assimétrica e não seqüencial #

Este é um comportamento que engana muitos desenvolvedores PHP. No PHP o código é executado em sequência, na primeira linha 1, depois na linha 2, e assim por diante. Em Javascript e particularmente em Node.js isso pode não ser o caso. Você pode potencialmente colocar as coisas em segundo plano com um bom uso de promessas e callbacks.

Below é um exemplo de código modificado com uma explicação tirada da minha repo:

async function getExternal(fromCurrency, toCurrency, onDate) {
const rate = await getRate(fromCurrency, toCurrency, onDate);
db.query(
`INSERT INTO exchange_rates (from_currency, to_currency, rate, on_date) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE rate = ?`,

).then(result => {
if (result.affectedRows === 0) {
console.error(`Exchange rate of ${rate} for ${fromCurrency} to ${toCurrency} on ${onDate} could not be saved`);
}
}).catch(err => {
console.log(`Error while writing to db: `, err);
}); //this is done async for the API to respond faster
console.log(`Fetched exchange rate of ${rate} for ${fromCurrency} to ${toCurrency} of ${onDate} from the API`);
return { fromCurrency, toCurrency, onDate, rate };
}

Se você olhar mais de perto, aquele olhar inocente db.query na linha 3, foi empurrado para segundo plano. Então ele irá executar como abaixo:

  1. Encerrar taxa
  2. Executar consulta de inserção em segundo plano
  3. Embora o insert esteja rodando a função já são retornados a taxa
  4. Se houver um problema na consulta de inserção, ele é logado na captura

Não há nenhuma maneira fora da caixa para fazer algo assim em PHP. Esta é a primeira coisa que atrapalha os desenvolvedores do PHP. Isso torna mais difícil entender o Node.js para desenvolvedores de PHP. Este comportamento assíncrono de execução de código também torna mais difícil encontrar o rastreamento correto da pilha em caso de erros no Node.js.

Para ser honesto, em 2020 você pode facilmente usar o async await. Mesmo sendo sintático no Promises, ele torna a programação assíncrona muito mais fácil. Quando eu comecei na era do Nodo 4/6 por volta de 2016 com callbacks e Promises era um jogo de bola completamente diferente. Ainda assim, cuidado quando não usar async-await (como acima) e apenas ir com promessas, então e pegar. Mas não se deixe enredar no inferno das promessas no processo. O inferno das promessas é como a próxima iteração do callback hell.

Dica Pro: Para saber quais recursos do ES6 você pode usar com qual versão do Node.js, confira em node.green.

Anoutra Dica Pro:

Versões do Node.js são LTS, as estranhas não são. Então use o Nó 14 ou 16 não 13 ou 15 na produção.

Vai um pouco mais fundo na execução não seqüencial, promessas e o poder que ele tem desempenha um papel importante aqui. A capacidade de fazer coisas simultâneas é ótima em Node.js e javascript em geral.

Node.js promete possibilidades #

Promessas sendo assíncronas, você pode executá-las concomitantemente. Existem formas de o fazer. Você pode correr 3 promessas e obter os resultados do mais rápido. Você pode até fazer promise.all onde se uma promessa for rejeitada, ela pára toda a operação. Por favor leia mais sobre Promise.race, promise.all e promise.any nesta grande comparação.

Com isso em mente, você pode tentar outras bibliotecas NPM para limitar a simultaneidade de promessas ou mesmo filtrar através de promessas simultaneamente. Você pode fazer um pouco disso com ReactPHP. Mas ele não está incluído no PHP nativo, nem mesmo no PHP 8. Isto é algo novo para envolver sua cabeça em Node.js para desenvolvedores PHP.

Vamos prosseguir para o próximo ponto, o processo não precisa morrer em Node.js como em PHP.

O processo em Node.js é de longa duração, ao contrário do PHP #

PHP é para morrer não no sentido de que ele não será usado. No sentido de que todos os processos PHP devem morrer. O PHP não é realmente projetado para tarefas/processos de longa duração. No PHP quando uma nova requisição HTTP chega no início do processamento, depois de enviar a resposta de volta o processo é morto. É assim que o PHP funciona. Isso cria a necessidade de FPM e outros servidores. Você pode argumentar que o PHP não tinha servidor por projeto 20+ anos atrás. Eu deixo isso para você.

No outro lado, Node.js é um processo de longa duração. Isso permite que você compartilhe informações entre requisições, já que o mesmo servidor/processo está lidando com várias requisições. Com um processo de longa duração, você pode facilmente explorar coisas como memoization on memory e connection pooling para um banco de dados. Isso abre outras possibilidades como a contagem do número de requisições simultâneas nesse processo, por exemplo.

Memoization exemplo #

If you don’t know Memoization.

Memoization is a higher-order function that caches another function. Ela pode transformar algumas funções lentas em funções rápidas. Ela salva o resultado de uma chamada de função após a primeira vez no cache, então se você chamar a função novamente com os mesmos argumentos, ela irá encontrá-la no cache.

Ela pode ser usada no Node.js mas não no PHP nativamente. É possível alguma alternativa no PHP como salvar o valor de retorno da função em Redis.

Below é uma amostra de código de memorização em uma rota expressa com p-memoize:

const ONE_MINUTE_IN_MS = 60000;
const options = {
maxAge: ONE_MINUTE_IN_MS,
cacheKey: (arguments_) => arguments_.join(','),
};
app.get('/api/products', async (req, res, next) => {
try {
const memGetProducts = pMemoize(products.getMultiple, options);
res.json(await memGetProducts(req.query.page || 1, req.query.search));
} catch (err) {
next(err);
}
});

A vantagem clara disto é menos carga na datastore. Durante 1 minuto, ele responderá de volta com a mesma resposta para os mesmos parâmetros. A saída da função products.getMultiple é armazenada em cache na memória durante um minuto. Isto torna as respostas muito rápidas.

Connection Pool example with MySQL #

Outra coisa que não é possível por causa de um processo de morte em PHP é o pooling de conexões. Como na Wikipedia:

Em engenharia de software, um pool de conexões é um cache de conexões de banco de dados mantido para que as conexões possam ser reutilizadas quando futuros pedidos para o banco de dados forem necessários. Os pools de conexões são usados para melhorar o desempenho da execução de comandos em um banco de dados.

Então, você terá 5 conexões em um pool e se você quiser executar 5 consultas ao banco de dados, isso pode ser feito ao mesmo tempo. Isto poupa tempo tanto para conectar ao banco de dados como para executar a consulta. Isto é fácil de fazer em Node.js mas não é facilmente possível em PHP.

Tenha em mente o número de conexões disponíveis e para manter o tamanho ideal do seu pool de conexões.

Por exemplo, se você está usando Kubernetes e sua aplicação tem 5 pods com um pool de conexões de tamanho 2. Isso significa que a sua base de dados terá sempre 10 ligações abertas mesmo que não existam consultas a serem executadas.

Tempo para um exemplo de pool de ligações com base de dados MySQL com módulo npm MySQL:

var pool = mysql.createPool({
connectionLimit : 5,
host : 'example.org',
user : 'app',
password : 'pass',
database : 'schema'
});
for(var i=0;i<5;i++){
pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
if (err) {
throw err;
}
console.log(rows.solution); //Shows 2
});
}

O código acima irá correr a mesma consulta 5 vezes em paralelo com 5 ligações MySQL tiradas do pool de ligações. Eu gostaria de poder fazer isso em PHP fora da caixa.

Na minha experiência, Node.js funciona muito bem com o Mysql. Se você quiser tentar o pool de conexões com Mongo DB, aqui está um exemplo de Mongo.

Com um processo longo como desenvolvedor você precisa ter mais cuidado com vazamentos de memória e fazer bem as coisas de manutenção da casa.

Aqui é onde o Node.js para desenvolvedores de PHP precisa de um pouco de mudança ao pensar sobre como o código é executado. Por outro lado, esta é uma grande vantagem em Node.js para desenvolvedores PHP.

Debugging é mais fácil em Node.js do que em PHP #

Line by line code debugging é uma parte importante da experiência do desenvolvedor para qualquer linguagem de programação. Para depurar código PHP, você pode usar add ons como X-Debug com algumas configurações do IDE. X-Debug é, no mínimo, um desafio para se configurar. Você tem que instalá-lo, ativar a extensão. Depois disso, configure-o corretamente com um IDE como o PHPStorm.

Basicamente, fácil é a última coisa que você dirá sobre como fazer o X-debug rodar. A menos que esteja tudo bem configurado com um container docker e as configurações da IDE também sejam fáceis de carregar.

Por outro lado, rodar o depurador nativo do nó ou até mesmo o ndb é muito mais fácil comparado ao PHP e X-debug. Com o uso do código VS, a aplicação de depuração do Node.js é tão fácil que até um homem das cavernas pode fazê-lo.

Open up Preferences > Settings and in the search box type in “node debug”. Sob a aba Extensões, deve haver uma extensão intitulada “Node debug”. A partir daqui, clique na primeira caixa: Debug > Nó: Auto Attach e defina o drop-down para “on”. Você está quase pronto para ir agora. Sim, é realmente tão fácil.

Então defina alguns pontos de quebra no código VS com digamos index.js e no tipo de terminal node --inspect index.js.

BOOM! Seu depurador passo a passo Node.js está rodando bem no editor de código VS sem muito esforço. Uma boa diferença do PHP, não há necessidade de instalar uma extensão diferente, habilitá-la e configurá-la para poder depurar um programa. Não há necessidade de instalar uma extensão extra é um benefício encontrado no Node.js para desenvolvedores PHP.

O próximo ponto é também sobre uma melhor experiência do desenvolvedor enquanto atualizando até mesmo múltiplas versões principais da linguagem.

Atualizações de versões maiores no Node.js é uma experiência perfeita sobre o PHP #

Pular até mesmo múltiplas versões principais no Node.js é uma experiência perfeita. A atualização do PHP 5.x para o PHP 7.x é um processo de uma semana para um mês, dependendo do tamanho e complexidade do projeto.

Na minha experiência pessoal, eu atualizei o Node.js microservices das versões 0.12 para 4 no passado. Recentemente eu atualizei uma aplicação do Node.js 10 para 14. Todas as minhas atualizações da versão principal do Node.js foram fáceis.

algumas pequenas mudanças no pacote.json foram os únicos pequenos problemas que encontrei. Após a implementação, raramente houve problemas relacionados à compatibilidade de código. Como um bônus adicional, a performance normalmente era melhor atualizando as versões maiores.

Por outro lado, atualizar o PHP não tem sido fácil. A atualização de versões menores para uma aplicação do PHP 5.4 para 5.6 não foi muito complicada. Mas, ir do PHP 5.6 para a 7.2 para uma aplicação relativamente grande foi uma dor. Levou muito tempo, e precisava de várias mudanças no composer.json. Também foi uma tarefa difícil testá-lo. O lado bom de uma grande atualização de versão no PHP foi certamente o aumento de performance.

Apenas uma nota aqui, as aplicações PHP com as quais trabalhei eram mais antigas que as aplicações Node.js. A sua experiência pode certamente ser diferente da minha.

Dockerizing a Node.js application is a breeze compared to PHP #

A popularidade do Docker tem vindo a aumentar constantemente nos últimos 5 anos. Ela mudou a forma como nós engenheiros de software trabalhamos desde o seu lançamento. Você deve usar o Docker para desenvolvimento local também. Com isso em mente, Dockerizar uma aplicação PHP pode ser uma tarefa difícil, dependendo de como os componentes estão dispostos e da complexidade da aplicação. Por outro lado, para a dockerizing de uma aplicação Node.js o esforço é menor e o processo é uma brisa.

Below é um exemplo de um dockerfile para uma aplicação PHP Laravel com Apache.

FROM composer:1.9.0 as build
WORKDIR /app
COPY . /app
RUN composer global require hirak/prestissimo && composer install
FROM php:7.3-apache-stretch
RUN docker-php-ext-install pdo pdo_mysql
EXPOSE 8080
COPY --from=build /app /var/www/
COPY docker/000-default.conf /etc/apache2/sites-available/000-default.conf
COPY .env.example /var/www/.env
RUN chmod 777 -R /var/www/storage/ && \
echo "Listen 8080" >> /etc/apache2/ports.conf && \
chown -R www-data:www-data /var/www/ && \
a2enmod rewrite

A coisa boa com esta imagem Docker para Laravel é que o PHP está empacotado com apache na mesma imagem. Pode-se argumentar se esta é uma maneira melhor de fazer isso do que dividir o PHP e o Apache em duas imagens docker.

Notem também a construção da docker multi-estágio na imagem docker acima. A instalação do compositor é feita em uma imagem diferente e a saída é copiada para a imagem principal. Se tivéssemos usado PHP-FPM e Nginx em diferentes imagens de docker, teria sido mais complexo. Haveria a necessidade de gerenciar duas imagens de docker distintas.

Agora é hora de dar uma olhada em um Dockerfile Node.js.

FROM node:14-alpine as base
WORKDIR /src
COPY package.json package-lock.json /src/
COPY . /src
EXPOSE 8080
FROM base as production
ENV NODE_ENV=production
RUN npm install
CMD
FROM base as dev
ENV NODE_ENV=development
RUN npm config set unsafe-perm true && npm install -g nodemon
RUN npm install
CMD

Como o Node.js tem um servidor web embutido, o Dockerfile é muito mais limpo.

Quando você instala o nó, o npm é empacotado com ele. Isso elimina a necessidade de instalar pacotes em um estágio diferente na construção da docker.

No Dockerfile acima a construção da docker multi-estágio é usada para separar as imagens da docker de produção e desenvolvimento. Ter o gerenciador de pacotes (npm) empacotado e ter o servidor web como parte da linguagem/runtime é algo diferente no Node.js para desenvolvedores de PHP. Se você está mais interessado em Dockering a Node.js appicaiton passo a passo siga este tutorial.

Conclusion #

Quando se usa Node.js para desenvolvedores PHP, é necessário uma mudança leve no pensamento para explorar bem os poderes do Node.js. O Node.js não é uma bala de prata. Há desvantagens e ele precisa se adaptar a diferentes formas de execução de código.

Com certeza, há alguns benefícios de usar Node.js para desenvolvedores de PHP como programação async e concurrency. Outras vantagens derivam do Node.js processo sendo de longa duração.

Eu espero que este post ajude você a obter mais do Node.js como um desenvolvedor PHP experiente.

Deixe uma resposta

O seu endereço de email não será publicado.