Node.js per sviluppatori PHP: 5 aspetti pragmatici da conoscere con esempi di codice

Mentre la popolarità di Node.js sta aumentando, la trazione di PHP sta diminuendo. Con questo contesto, questo post elaborerà 5 aspetti pratici da conoscere assolutamente nell’uso di Node.js per gli sviluppatori PHP. Queste saranno cose di cui nessuno parla o scrive, è ora di andare.

  1. Node.js per sviluppatori PHP (non Node.js vs PHP)
  2. Node.js per sviluppatori PHP il lato pratico
    1. L’esecuzione del codice Node.js è asincrona e non sequenziale
      1. Node.js promette possibilità
    2. Il processo di Node.js è di lunga durata, a differenza di PHP
      1. Esempio di memorizzazione
      2. Esempio di pool di connessioni con MySQL
    3. Il debug è più facile in Node.js che in PHP
    4. Gli aggiornamenti di versione in Node.js sono senza soluzione di continuità rispetto a PHP
    5. Il dock di un’applicazione Node.js è un gioco da ragazzi rispetto a PHP
  3. Conclusione

Node.js per sviluppatori PHP (non Node.js vs PHP) #

Questo pezzo è una lista di cose che uno sviluppatore PHP deve sapere e imparare per usare Node.js efficacemente. Al contrario, questo post non è un articolo su Node.js vs PHP in cui PHP viene picchiato. Ho usato entrambi i linguaggi. Ho iniziato a scrivere più Node.js nel 2016. Quando ho iniziato ho affrontato alcune difficoltà perché ero abituato a PHP al lavoro per più di 7 anni prima. C’è stato un libro rilasciato verso la fine del 2012 che copre Node.js per gli sviluppatori PHP.

Questo post sul blog non parlerà di cosa sia PHP o Node.js, potete leggerlo in altri post. Non parlerò nemmeno molto del Non-Blocking I/O o dell’event loop. Ancora, alcuni di questi argomenti saranno sfiorati quando si parlerà degli aspetti pratici della scrittura di buon codice Node.js.

Node.js per sviluppatori PHP il lato pratico #

PHP è in vita dal 1995 e, secondo quanto riferito, è ancora usato dal 79,% dei siti web monitorati da W3tech (non posso dire se sia l’intera Internet). Quindi è molto probabile che abbiate usato PHP o distribuito qualcosa scritto in PHP. Per esempio con una tendenza crescente:

WordPress è usato dal 63,7% di tutti i siti web il cui sistema di gestione dei contenuti conosciamo. Questo è il 39,0% di tutti i siti web monitorati da W3Tech.

D’altra parte, Node.js è stato rilasciato nel 2009. Grandi aziende tecnologiche come Linked In e Paypal hanno iniziato ad adottarlo dal 2011 al 2013 per vari motivi come i microservizi. Come da Stack Overflow developer survey del 2020:

Per il secondo anno di fila, Node.js occupa il primo posto, poiché è usato dalla metà degli intervistati.

Non è un segreto che Node.js sta diventando molto popolare negli ultimi 5 anni.

Quindi, come sviluppatore PHP, queste sono 5 cose pratiche da sapere per essere un grande ingegnere software Node.js. Node.js per gli sviluppatori PHP è simile in un certo senso ma anche diverso in alcuni altri aspetti che sono descritti di seguito:

L’esecuzione del codice Node.js è asincrona e non sequenziale #

Questo è un comportamento che inganna molti sviluppatori PHP. In PHP il codice viene eseguito in sequenza, prima la linea 1 poi la 2, e così via. In Javascript e in particolare in Node.js questo potrebbe non essere il caso. Puoi potenzialmente mettere le cose in background con un buon uso di promesse e callback.

Di seguito un esempio di codice modificato con una spiegazione presa dal mio repo open source currency-api:

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 guardi meglio, quell’innocente db.query alla linea 3, è stato spinto in background. Quindi verrà eseguito come segue:

  1. Rileva il tasso
  2. Esegui la query di inserimento in background
  3. Mentre l’inserimento è in esecuzione la funzione ha già restituito il tasso
  4. Se c’è un problema nella query di inserimento viene registrato nel catch

Non esiste un modo fuori dalla scatola per fare qualcosa del genere in PHP. Questa è la prima cosa che blocca gli sviluppatori PHP. Rende più difficile capire Node.js per gli sviluppatori PHP. Questo comportamento di esecuzione asincrona del codice rende anche più difficile trovare la giusta traccia dello stack in caso di errori in Node.js.

Ad essere onesti, nel 2020 si può facilmente usare async await. Anche se è uno zucchero sintattico su Promises, rende la programmazione asincrona molto più facile. Quando ho iniziato nell’era di Node 4/6 intorno al 2016 con callback e Promises era un gioco di palle completamente diverso. Ancora, attenzione a quando non usare async-await (come sopra) e andare solo con promesse, poi e catch. Non rimanere invischiato nell’inferno delle promesse nel processo però. L’inferno delle promesse è come l’iterazione successiva dell’inferno delle callback.

Pro tip: Per sapere quali funzionalità ES6 puoi usare con quale versione di Node.js, controlla su node.green.

Un altro Pro Tip:

Le versioni di Node.js pari sono LTS, quelle dispari no. Quindi usate Node 14 o 16, non 13 o 15 in produzione.

Andando un po’ più a fondo nell’esecuzione non sequenziale, le promesse e il potere che hanno giocano un ruolo importante qui. La capacità di fare cose concorrenti è grande in Node.js e in javascript in generale.

Possibilità di promesse in Node.js #

Promesse essendo asincrone, è possibile eseguirle in modo concorrente. Ci sono modi per farlo. Potresti far correre 3 promesse e ottenere i risultati da quella più veloce. Si può anche fare promise.alldove se una promessa viene rifiutata, si ferma l’intera operazione. Leggi di più su Promise.race, promise.all e promise.any in questo grande confronto.

Con questo in mente, puoi provare altre librerie NPM per limitare la concorrenza delle promesse o anche filtrare le promesse in modo concorrente. Puoi fare alcune di queste cose con ReactPHP. Ma non è incluso nel PHP nativo, nemmeno in PHP 8. Questo è qualcosa di nuovo da avvolgere la testa in Node.js per gli sviluppatori PHP.

Passiamo al punto successivo, il processo non ha bisogno di morire in Node.js come in PHP.

Il processo Node.js è di lunga durata, a differenza di PHP #

PHP è destinato a morire non nel senso che non sarà utilizzato. Nel senso che tutti i processi PHP devono morire. PHP non è davvero progettato per compiti/processi di lunga durata. In PHP quando arriva una nuova richiesta HTTP inizia l’elaborazione, dopo aver inviato la risposta il processo viene ucciso. Questo è il modo in cui funziona PHP. Questo crea la necessità di FPM e di altri server. Puoi sostenere che PHP era senza server per progettazione 20+ anni fa. Lo lascio a te.

Dall’altro lato, Node.js è un processo di lunga durata. Ciò consente di condividere le informazioni tra le richieste poiché lo stesso server/processo sta gestendo più richieste. Con un processo a lunga esecuzione, si possono facilmente sfruttare cose come la memorizzazione della memoria e il pooling delle connessioni per un database. Si aprono altre possibilità come il conteggio del numero di richieste concorrenti su quel processo per esempio.

Esempio di memorizzazione #

Se non conoscete la memorizzazione.

La memorizzazione è una funzione di ordine superiore che memorizza un’altra funzione. Può trasformare alcune funzioni lente in veloci. Salva il risultato di una chiamata di funzione dopo la prima volta nella cache, quindi se si chiama di nuovo la funzione con gli stessi argomenti, la troverà nella cache.

Può essere usata in Node.js ma non in PHP nativamente. Alcune soluzioni sono possibili in PHP, come salvare il valore di ritorno della funzione in Redis.

Di seguito è riportato un esempio di codice di memoizzazione su un percorso espresso con 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);
}
});

Il chiaro vantaggio di questo è un minor carico sul datastore. Per 1 minuto, risponderà con la stessa risposta per gli stessi parametri. L’output della funzione products.getMultiple viene salvato in memoria per un minuto. Questo rende le risposte molto veloci.

Esempio di pool di connessioni con MySQL #

Un’altra cosa che non è possibile a causa di un processo morente in PHP è il pooling delle connessioni. Come da Wikipedia:

In ingegneria del software, un pool di connessioni è una cache di connessioni al database mantenuta in modo che le connessioni possano essere riutilizzate quando sono richieste future al database. I pool di connessioni sono usati per migliorare le prestazioni dell’esecuzione dei comandi su un database.

Quindi, avrete 5 connessioni in un pool e se volete eseguire 5 query al database può essere fatto contemporaneamente. Questo fa risparmiare tempo sia per la connessione al database che per l’esecuzione della query. Questo è facile da fare in Node.js ma non è facilmente possibile in PHP.

Stare attenti al numero di connessioni disponibili e mantenere la dimensione del pool di connessioni ottimale.

Per esempio, se state usando Kubernetes e la vostra applicazione ha 5 pod con una dimensione del pool di connessioni di 2. Questo significa che il vostro database avrà sempre 10 connessioni in un pool. Questo significa che il vostro database avrà sempre 10 connessioni aperte anche se non ci sono query in esecuzione.

Per un esempio di connection pool con database MySQL con il modulo MySQL npm:

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
});
}

Il codice sopra riportato eseguirà la stessa query 5 volte in parallelo con 5 connessioni MySQL prese dal connection pool. Avrei voluto poterlo fare in PHP out of the box.

Nella mia esperienza, Node.js funziona molto bene con Mysql. Se vuoi provare il connection pooling con Mongo DB, qui c’è un esempio di Mongo.

Con un processo di lunga durata come sviluppatore devi stare più attento alle perdite di memoria e a fare bene le cose di manutenzione.

E’ qui che Node.js per gli sviluppatori PHP ha bisogno di un bel cambiamento nel pensare a come il codice viene eseguito. D’altra parte, questo è un grande vantaggio in Node.js per gli sviluppatori PHP.

Il debug è più facile in Node.js che in PHP #

Il debug del codice linea per linea è una parte importante dell’esperienza dello sviluppatore per qualsiasi linguaggio di programmazione. Per eseguire il debug del codice PHP, è possibile utilizzare add on come X-Debug con alcune impostazioni dell’IDE. X-Debug è a dir poco impegnativo da configurare. Devi installarlo, abilitare l’estensione. Dopo di che configurarlo correttamente con un IDE come PHPStorm.

Fondamentalmente, facile è l’ultima cosa che dirai per far funzionare X-debug. A meno che non sia tutto configurato bene con un contenitore docker e le impostazioni dell’IDE siano anche facili da caricare.

D’altra parte, eseguire il debugger nativo di node o anche ndb è molto più facile rispetto a PHP e X-debug. Con l’uso di VS Code, il debug dell’applicazione Node.js è così facile che anche un uomo delle caverne può farlo.

Apri Preferenze > Impostazioni e nella casella di ricerca digita “node debug”. Sotto la scheda Estensioni, dovrebbe esserci un’estensione intitolata “Node debug”. Da qui, clicca sulla prima casella: Debug > Node: Auto Attach e imposta il drop-down su “on”. Ora sei quasi pronto a partire. Sì, è davvero così facile.

Poi imposta alcuni breakpoint sul codice VS con diciamo index.js e nel terminale digita node --inspect index.js.

BOOM! Il vostro debugger di Node.js passo dopo passo funziona bene sull’editor di codice VS senza molto sforzo. Una buona differenza rispetto a PHP, non c’è bisogno di installare un’altra estensione, abilitarla e configurarla per essere in grado di eseguire il debug di un programma. Non c’è bisogno di installare un’estensione extra è un vantaggio trovato in Node.js per gli sviluppatori PHP.

Il prossimo punto riguarda anche una migliore esperienza dello sviluppatore durante l’aggiornamento anche di più versioni principali del linguaggio.

Gli aggiornamenti delle versioni principali in Node.js sono senza soluzione di continuità rispetto a PHP #

Il salto anche di più versioni principali in Node.js è un’esperienza senza problemi. L’aggiornamento da PHP 5.x a PHP 7.x è un processo che dura da una settimana a un mese, a seconda delle dimensioni e della complessità del progetto.

Nella mia esperienza personale, ho aggiornato microservizi Node.js dalla versione 0.12 alla 4 in passato. Recentemente ho aggiornato un’applicazione da Node.js 10 a 14. Tutti i miei aggiornamenti della versione principale di Node.js sono stati facili.

Alcuni cambiamenti minori di package.json sono stati gli unici piccoli problemi che ho incontrato. Dopo la distribuzione, raramente ci sono stati problemi relativi alla compatibilità del codice. Come bonus aggiuntivo, le prestazioni erano solitamente migliori aggiornando le versioni maggiori.

D’altra parte, aggiornare PHP non è stato facile. L’aggiornamento della versione minore per un’applicazione da PHP 5.4 a 5.6 non era molto complicato. Ma passare da PHP 5.6 a 7.2 per un’applicazione relativamente grande è stata una sofferenza. Ci voleva molto tempo e aveva bisogno di più modifiche al composer.json. È stato anche un compito difficile da testare. Il lato positivo di un importante aggiornamento di versione in PHP è stato sicuramente l’aumento delle prestazioni.

Solo una nota, le applicazioni PHP con cui ho lavorato erano più vecchie delle applicazioni Node.js. La tua esperienza può sicuramente essere diversa dalla mia.

Dockerizzare un’applicazione Node.js è un gioco da ragazzi rispetto a PHP #

La popolarità di Docker è stata in costante aumento negli ultimi 5 anni. Ha cambiato il modo in cui noi ingegneri del software lavoriamo dal suo rilascio. Dovreste usare Docker anche per lo sviluppo locale. Con questo in mente, Dockerizzare un’applicazione PHP può essere un compito difficile a seconda di come sono disposti i componenti e della complessità dell’applicazione. Al contrario, per dockerizzare un’applicazione Node.js lo sforzo è minore e il processo è un gioco da ragazzi.

Di seguito un esempio di dockerfile per un’applicazione PHP Laravel con 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

La cosa buona di questa immagine Docker per Laravel è che PHP è in bundle con Apache nella stessa immagine. Si può discutere se questo sia un modo migliore di farlo che dividere PHP e Apache in due immagini docker.

Si noti anche la costruzione docker a più stadi nell’immagine docker di cui sopra. L’installazione di Composer viene fatta in un’altra immagine e l’output viene copiato in quella principale. Se avessimo usato PHP-FPM e Nginx in diverse immagini docker, sarebbe stato più complesso. Ci sarebbe stato bisogno di gestire due immagini docker distinte.

Ora è il momento di dare un’occhiata a un Dockerfile di 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

Come Node.js ha un server web integrato, il Dockerfile è molto più pulito.

Quando si installa node, npm è in bundle con esso. Questo elimina la necessità di installare i pacchetti in una fase diversa della compilazione docker.

Nel Dockerfile di cui sopra la compilazione docker a più fasi è usata per separare le immagini docker di produzione e di sviluppo. Avere il gestore di pacchetti (npm) in bundle e avere il web-server come parte del linguaggio/runtime è qualcosa di diverso in Node.js per gli sviluppatori PHP. Se sei interessato a fare il Dockering di un’applicazione Node.js passo dopo passo segui questo tutorial.

Conclusione #

Quando si usa Node.js per gli sviluppatori PHP è necessario un leggero cambiamento di pensiero per sfruttare bene i poteri di Node.js. Node.js non è una pallottola d’argento. Ci sono degli svantaggi e ha bisogno di adattarsi a diversi modi di esecuzione del codice.

Certamente, ci sono alcuni vantaggi nell’usare Node.js per gli sviluppatori PHP come la programmazione asincrona e la concorrenza. Altri vantaggi derivano dal fatto che Node.js essendo un processo di lunga durata.

Spero che questo post ti aiuti a ottenere di più da Node.js come sviluppatore PHP esperto.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.