Node.js pentru dezvoltatorii PHP: 5 aspecte pragmatice pe care trebuie să le cunoașteți neapărat cu exemple de cod

În timp ce popularitatea Node.js este în creștere, tracțiunea PHP scade. În acest context, această postare va detalia 5 aspecte practice pe care trebuie să le cunoască neapărat dezvoltatorii PHP în legătură cu utilizarea Node.js. Acestea vor fi lucruri despre care nimeni nu vorbește sau scrie, e timpul să ne apucăm de treabă.

  1. Node.js pentru dezvoltatorii PHP (nu Node.js vs PHP)
  2. Node.js pentru dezvoltatorii PHP partea practică
    1. Executarea codului Node.js este asincronă și nesevențială
      1. Node.js promite posibilități
    2. Procesul Node.js este de lungă durată, spre deosebire de PHP
      1. Exemplu de memorizare
      2. Exemplu de pool de conexiuni cu MySQL
    3. Debugging-ul este mai ușor în Node.js decât în PHP
    4. Actualizările majore de versiuni în Node.js sunt fără probleme față de PHP
    5. Dockerizarea unei aplicații Node.js este floare la ureche în comparație cu PHP
  3. Concluzie

Node.js pentru dezvoltatorii PHP (nu Node.js vs PHP) #

Acest articol este o listă de lucruri pe care tu, ca dezvoltator PHP, trebuie să le știi și să le înveți pentru a folosi Node.js în mod eficient. Dimpotrivă, acest articol nu este o scriere Node.js vs PHP în care PHP este bătut. Am folosit ambele limbaje. Am început să scriu mai mult Node.js în 2016. Când am început m-am confruntat cu unele dificultăți, deoarece eram obișnuit cu PHP la locul de muncă de mai bine de 7 ani înainte de asta. A existat o carte lansată spre sfârșitul anului 2012 care acoperă Node.js pentru dezvoltatorii PHP.

Acest articol de blog nu va vorbi despre ce este PHP sau Node.js, puteți citi despre asta în alte articole. De asemenea, nu voi vorbi prea mult despre Non-Blocking I/O sau despre bucla de evenimente. Totuși, o parte din ele vor fi trecute cu buretele atunci când se va discuta despre aspectele practice ale scrierii unui cod Node.js bun.

Node.js pentru dezvoltatorii PHP partea practică #

PHP trăiește din 1995 și se pare că este încă folosit de 79,% dintre site-urile web monitorizate de W3tech (nu pot spune cu adevărat dacă este tot internetul). Deci sunt șanse foarte mari să fi folosit PHP sau să fi implementat ceva scris în PHP. De exemplu, cu o tendință în creștere:

WordPress este folosit de 63,7% din toate site-urile web al căror sistem de gestionare a conținutului îl cunoaștem. Aceasta reprezintă 39,0% din toate site-urile web monitorizate de W3Tech.

Pe de altă parte, Node.js a fost lansat în 2009. Marile companii de tehnologie, cum ar fi Linked In și Paypal, au început să îl adopte prin 2011 – 2013 din diverse motive, cum ar fi microserviciile. Conform sondajului dezvoltatorilor Stack Overflow din 2020:

Pentru al doilea an consecutiv, Node.js ocupă primul loc, deoarece este folosit de jumătate dintre respondenți.

Nu este un secret că Node.js devine foarte popular în ultimii 5 ani.

Atunci, ca dezvoltator PHP, acestea sunt 5 lucruri practice pe care trebuie să le știi pentru a fi un mare inginer software Node.js. Node.js pentru dezvoltatorii PHP este similar într-un anumit sens, dar, de asemenea, diferit în alte câteva aspecte, unele sunt descrise mai jos:

Executarea codului Node.js este asincronă și nesevențială #

Este un comportament care păcălește o mulțime de dezvoltatori PHP. În PHP codul se execută în secvență, la început linia 1, apoi 2, și așa mai departe. În Javascript și în special în Node.js s-ar putea ca acest lucru să nu se întâmple. Puteți, potențial, să puneți lucruri în fundal cu o bună utilizare a promisiunilor și a callback-urilor.

Mai jos este un exemplu de cod modificat cu o explicație luată din repo-ul meu 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 };
}

Dacă vă uitați mai atent, acel db.query nevinovat de la linia 3, a fost împins în fundal. Deci, se va executa ca mai jos:

  1. Obține rata
  2. Execută interogarea de inserție în fundal
  3. În timp ce inserția se execută, funcția este deja returnată rata
  4. Dacă există o problemă în interogarea de inserție, aceasta este înregistrată în catch

Nu există o modalitate de ieșire din cutie pentru a face așa ceva în PHP. Acesta este primul lucru care îi încurcă pe dezvoltatorii PHP. Face ca Node.js să fie mai greu de înțeles pentru dezvoltatorii PHP. Acest comportament de execuție asincronă a codului face, de asemenea, mai dificilă găsirea urmăririi corecte a stivei în caz de erori în Node.js.

Pentru a fi sincer, în 2020 puteți folosi cu ușurință async await. Chiar dacă este zahăr sintactic pe Promises, face programarea asincronă mult mai ușoară. Când am început în epoca Node 4/6, în jurul anului 2016, cu callback-uri și Promises, a fost un joc de mingi cu totul diferit. Totuși, aveți grijă când să nu folosiți async-await (ca mai sus) și să mergeți doar cu promisiuni, apoi și catch. Totuși, nu vă încurcați în iadul promisiunilor în acest proces. Iadul promisiunilor este ca următoarea iterație a iadului callback.

Tip Pro: Pentru a ști ce caracteristici ES6 puteți folosi cu ce versiune de Node.js, verificați la node.green.

Un alt pont Pro:

Versiunile Node.js sunt LTS, cele ciudate nu sunt. Așa că folosiți Node 14 sau 16, nu 13 sau 15 în producție.

Dezvoltând puțin mai adânc execuția non-secvențială, promisiunile și puterea pe care o are joacă un rol important aici. Capacitatea de a face lucruri concurente este grozavă în Node.js și în javascript în general.

Posibilități ale promisiunilor Node.js #

Promisiunile fiind asincrone, le puteți executa concomitent. Există modalități de a face acest lucru. Ați putea concura 3 promisiuni și să obțineți rezultatele de la cea mai rapidă. Puteți chiar să faceți promise.all în cazul în care dacă o promisiune este respinsă, se oprește întreaga operațiune. Vă rugăm să citiți mai multe despre Promise.race, promise.all și promise.any în această comparație grozavă.

Din acest punct de vedere, puteți încerca alte biblioteci NPM pentru a limita concurența promisiunilor sau chiar să filtrați prin promisiuni concomitent. Puteți face unele dintre acestea cu ReactPHP. Dar aceasta nu este inclusă în PHP nativ, nici măcar în PHP 8. Acesta este un lucru nou în Node.js pentru dezvoltatorii PHP.

Să trecem la următorul punct, procesul nu trebuie să moară în Node.js ca în PHP.

Procesul Node.js este de lungă durată, spre deosebire de PHP #

PHP este menit să moară nu în sensul că nu va fi folosit. În sensul că toate procesele PHP trebuie să moară. PHP nu este conceput cu adevărat pentru sarcini/procese cu durată lungă de execuție. În PHP, atunci când intră o nouă cerere HTTP începe procesarea, după ce trimite răspunsul înapoi, procesul este ucis. Acesta este modul în care funcționează PHP. Asta creează nevoia de FPM și de alte servere. Puteți susține că PHP a fost proiectat fără server în urmă cu peste 20 de ani. Vă las pe voi să decideți asta.

De cealaltă parte, Node.js este un proces de lungă durată. Acest lucru vă permite să partajați informații între cereri, deoarece același server/proces se ocupă de mai multe cereri. Cu un proces cu execuție îndelungată, puteți exploata cu ușurință lucruri precum memoizarea pe memorie și pooling-ul de conexiuni pentru o bază de date. Aceasta deschide alte posibilități, cum ar fi, de exemplu, numărarea nr. de cereri concurente pe acel proces.

Exemplu de memoizare #

Dacă nu cunoașteți memoizarea.

Memoizarea este o funcție de ordin superior care pune în cache o altă funcție. Ea poate transforma unele funcții lente în funcții rapide. Salvează rezultatul apelării unei funcții după prima dată în memoria cache, astfel încât, dacă apelați din nou funcția cu aceleași argumente, aceasta o va găsi în memoria cache.

Poate fi utilizată în Node.js, dar nu și în PHP în mod nativ. Unele soluții de rezolvare sunt posibile în PHP, cum ar fi salvarea valorii de întoarcere a funcției în Redis.

Mai jos este o mostră de cod de memoizare pe o rută expres cu 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);
}
});

Avantajul clar al acestui lucru este încărcarea mai mică a depozitului de date. Timp de 1 minut, acesta va răspunde înapoi cu același răspuns pentru aceiași parametri. Rezultatul funcției products.getMultiple este stocat în memorie timp de un minut. Acest lucru face ca răspunsurile să fie foarte rapide.

Exemplu de pool de conexiuni cu MySQL #

Un alt lucru care nu este posibil din cauza unui proces muribund în PHP este pooling-ul de conexiuni. Conform Wikipedia:

În ingineria software, un pool de conexiuni este o memorie cache a conexiunilor la o bază de date, menținută astfel încât conexiunile să poată fi reutilizate atunci când sunt necesare cereri viitoare către baza de date. Pool-urile de conexiuni sunt folosite pentru a îmbunătăți performanța executării comenzilor pe o bază de date.

Atunci, veți avea 5 conexiuni într-un pool și dacă doriți să executați 5 interogări în baza de date, aceasta se poate face concomitent. Acest lucru economisește timp atât pentru conectarea la baza de date, cât și pentru executarea interogării. Acest lucru este ușor de realizat în Node.js, dar nu este ușor de realizat în PHP.

Să fiți atenți la numărul de conexiuni disponibile și să păstrați dimensiunea optimă a pool-ului de conexiuni.

De exemplu, dacă folosiți Kubernetes și aplicația dvs. are 5 poduri cu o dimensiune a pool-ului de conexiuni de 2. Asta înseamnă că baza dvs. de date va avea întotdeauna 10 conexiuni deschise, chiar dacă nu se execută nicio interogare.

Timp pentru un exemplu de pool de conexiuni cu baza de date MySQL cu modulul 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
});
}

Codul de mai sus va rula aceeași interogare de 5 ori în paralel cu 5 conexiuni MySQL luate din poolul de conexiuni. Mi-aș fi dorit să pot face acest lucru în PHP din start.

Din experiența mea, Node.js funcționează foarte bine cu Mysql. Dacă doriți să încercați pooling-ul de conexiuni cu Mongo DB, iată un exemplu Mongo.

Cu un proces de lungă durată, ca dezvoltator, trebuie să fiți mai atenți la scurgerile de memorie și să faceți bine lucrurile de menaj.

Acesta este punctul în care dezvoltatorii Node.js pentru PHP au nevoie de o schimbare destul de mare de gândire cu privire la modul în care este executat codul. Pe de altă parte, acesta este un mare avantaj în Node.js pentru dezvoltatorii PHP.

Depanarea este mai ușoară în Node.js decât în PHP #

Depanarea codului linie cu linie este o parte importantă a experienței dezvoltatorului pentru orice limbaj de programare. Pentru a depanarea codului PHP, puteți utiliza add-on-uri precum X-Debug cu unele setări IDE. X-Debug este cel puțin dificil de configurat. Trebuie să îl instalați, să activați extensia. După care să o configurați corespunzător cu un IDE cum ar fi PHPStorm.

În principiu, ușor este ultimul lucru pe care îl veți spune despre a face să funcționeze X-debug. Cu excepția cazului în care totul este bine configurat cu un container docker și setările IDE sunt, de asemenea, ușor de încărcat.

Pe de altă parte, rularea lui node native debugger sau chiar ndb este mult mai ușoară în comparație cu PHP și X-debug. Cu ajutorul VS Code, depanarea aplicației Node.js este atât de ușoară încât chiar și un om al cavernelor o poate face.

Deschideți Preferences > Settings și în caseta de căutare tastați „node debug”. În fila Extensions (Extensii), ar trebui să existe o extensie intitulată „Node debug”. De aici, faceți clic pe prima casetă: Debug > Node: Auto Attach și setați drop-down la „on”. Acum sunteți aproape gata de plecare. Da, chiar este atât de ușor.

Apoi setați niște puncte de întrerupere pe codul VS cu să zicem index.js și în terminal tastați node --inspect index.js.

BOOM! Depanatorul tău Node.js pas cu pas funcționează bine pe editorul VS Code fără prea mult efort. O diferență bună față de PHP, nu este nevoie să instalați o altă extensie, să o activați și să o configurați pentru a putea depana un program. Nu este nevoie să instalați o extensie suplimentară este un beneficiu găsit în Node.js pentru dezvoltatorii PHP.

Următorul punct se referă, de asemenea, la o experiență mai bună a dezvoltatorului în timpul actualizării chiar și a mai multor versiuni majore ale limbajului.

Actualizările de versiuni majore în Node.js sunt fără probleme față de PHP #

Saltul chiar și a mai multor versiuni majore în Node.js este o experiență fără probleme. Actualizarea de la PHP 5.x la PHP 7.x este un proces care durează de la o săptămână până la o lună, în funcție de mărimea și complexitatea proiectului.

În experiența mea personală, am actualizat microservicii Node.js de la versiunile 0.12 la 4 în trecut. Recent, am actualizat o aplicație de la Node.js 10 la 14. Toate actualizările mele de versiuni majore Node.js au fost ușoare.

Câteva modificări minore ale package.json au fost singurele mici probleme pe care le-am întâlnit. După implementare, rareori au existat probleme legate de compatibilitatea codului. Ca un bonus suplimentar, performanța a fost de obicei mai bună la actualizarea versiunilor majore.

Pe de altă parte, actualizarea PHP nu a fost ușoară. Actualizarea versiunilor minore pentru o aplicație de la PHP 5.4 la 5.6 nu a fost foarte greoaie. Dar, trecerea de la PHP 5.6 la 7.2 pentru o aplicație relativ mare a fost un chin. A durat mult timp și a necesitat mai multe modificări composer.json. A fost, de asemenea, o sarcină dificilă să o testăm. Partea bună a unui upgrade major de versiune în PHP a fost cu siguranță creșterea performanței.

Doar o notă aici, aplicațiile PHP cu care am lucrat erau mai vechi decât aplicațiile Node.js. Experiența dvs. poate fi cu siguranță diferită de a mea.

Dockerizarea unei aplicații Node.js este o briză în comparație cu PHP #

Popularitatea lui Docker a crescut constant în ultimii 5 ani. Acesta a schimbat modul în care noi, inginerii de software, lucrăm de la lansarea sa. Ar trebui să folosiți Docker și pentru dezvoltarea locală. Având în vedere acest lucru, Dockerizarea unei aplicații PHP poate fi o sarcină dificilă, în funcție de modul în care sunt dispuse componentele și de complexitatea aplicației. Dimpotrivă, pentru dockerizarea unei aplicații Node.js efortul este mai mic și procesul este floare la ureche.

Mai jos este un exemplu de fișier docker pentru o aplicație PHP Laravel cu 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

Ceea ce este bun la această imagine Docker pentru Laravel este că PHP este la pachet cu apache în aceeași imagine. Se poate argumenta dacă aceasta este o modalitate mai bună de a face acest lucru decât să împarți PHP și Apache în două imagini docker.

De asemenea, observați compilarea docker în mai multe etape în imaginea docker de mai sus. Instalarea Composer se face într-o imagine diferită și ieșirea este copiată în cea principală. Dacă am fi folosit PHP-FPM și Nginx în imagini docker diferite, ar fi fost mai complex. Ar fi fost nevoie să gestionăm două imagini docker distincte.

Acum este timpul să aruncăm o privire la un fișier Docker 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

Pentru că Node.js are un server web încorporat, fișierul Docker este mult mai curat.

Când instalați node, npm este inclus împreună cu acesta. Acest lucru elimină necesitatea de a instala pachete într-o etapă diferită în construcția docker.

În fișierul Docker de mai sus se utilizează construcția docker în mai multe etape pentru a separa imaginile docker de producție și de dezvoltare. Faptul de a avea managerul de pachete (npm) la pachet și de a avea serverul web ca parte a limbajului/runtime-ului este ceva diferit în Node.js pentru dezvoltatorii PHP. Dacă vă interesează mai mult Dockering-ul unei aplicații Node.js pas cu pas, urmați acest tutorial.

Concluzie #

Când se utilizează Node.js pentru dezvoltatorii PHP este nevoie de o ușoară schimbare de gândire pentru a exploata bine puterile Node.js. Node.js nu este un glonț de argint. Există dezavantaje și trebuie să se adapteze la diferite moduri de execuție a codului.

Cert este că există unele beneficii ale utilizării Node.js pentru dezvoltatorii PHP, cum ar fi programarea asincronă și concurența. Alte avantaje decurg din conceptul Node.js fiind de lungă durată.

Sper că această postare vă ajută să obțineți mai mult de la Node.js ca un dezvoltator PHP experimentat.

.

Lasă un răspuns

Adresa ta de email nu va fi publicată.