Node.js for PHP-udviklere: 5 must-know praktiske aspekter med kodeeksempler Node.js for PHP-udviklere:

Mens populariteten af Node.js er stigende, er PHP’s trækkraft på vej ned. Med den baggrund vil dette indlæg uddybe 5 must-know praktiske aspekter af brugen af Node.js for PHP-udviklere. Det vil være ting, som ingen taler eller skriver om, tid til at komme i gang.

  1. Node.js for PHP-udviklere (ikke Node.js vs PHP)
  2. Node.js for PHP-udviklere den praktiske side
    1. Node.js-kodeudførelse er asynkron og ikke-sekventiel
      1. Node.js lover muligheder
    2. Node.js-processen er langvarig, i modsætning til PHP
      1. Memoisering eksempel
      2. Connection Pool eksempel med MySQL
  3. Debugging er lettere i Node.js end i PHP
  4. Større versionsopgraderinger i Node.js er problemfri i forhold til PHP
  5. Dockerizing af en Node.js applikation er en leg sammenlignet med PHP
  • Konklusion
  • Node.js for PHP-udviklere (ikke Node.js vs. PHP) #

    Dette stykke er en liste over ting, du som PHP-udvikler skal vide og lære for at bruge Node.js effektivt. Tværtimod er dette indlæg ikke en Node.js vs. PHP-opskrivning, hvor PHP bliver bashet. Jeg har brugt begge sprog. Jeg begyndte at skrive mere Node.js i 2016. Da jeg startede, stod jeg over for nogle vanskeligheder, da jeg var vant til PHP på mit arbejde i mere end 7 år før det. Der blev udgivet en bog mod slutningen af 2012, der dækker Node.js for PHP-udviklere.

    Dette blogindlæg kommer ikke til at tale om, hvad PHP eller Node.js er, det kan du læse om i andre indlæg. Jeg vil heller ikke tale meget om Non-Blocking I/O eller event loop. Alligevel vil noget af det blive børstet igennem, når jeg diskuterer de praktiske aspekter af at skrive god Node.js-kode.

    Node.js for PHP-udviklere den praktiske side #

    PHP har været i live siden 1995 og bruges efter sigende stadig af 79,% af de websteder, der overvåges af W3tech (jeg kan ikke rigtig sige, om det hele internettet). Så chancerne er meget store for, at du har brugt PHP eller implementeret noget, der er skrevet i PHP. For eksempel med en stigende tendens:

    WordPress bruges af 63,7 % af alle de websteder, hvis indholdsstyringssystem vi kender. Det er 39,0 % af alle websteder, der overvåges af W3Tech.

    På den anden side blev Node.js udgivet i 2009. Store teknologivirksomheder som Linked In og Paypal begyndte at indføre det i 2011 til 2013 af forskellige årsager som f.eks. microservices. Ifølge Stack Overflow-udviklerundersøgelsen fra 2020:

    For andet år i træk indtager Node.js førstepladsen, da det bruges af halvdelen af respondenterne.

    Det er ikke nogen hemmelighed, at Node.js er blevet meget populært i de seneste 5 år.

    Så som PHP-udvikler er dette 5 praktiske ting, du skal vide for at være en god Node.js-softwareingeniør. Node.js for PHP-udviklere er ens i nogen forstand, men også anderledes i nogle andre aspekter nogle er beskrevet nedenfor:

    Node.js-kodeudførelse er asynkron og ikke-sekventiel #

    Dette er en adfærd, der snyder mange PHP-udviklere. I PHP kører koden i rækkefølge, først linje 1 derefter 2 osv. I Javascript og især i Node.js er det måske ikke tilfældet. Du kan potentielt sætte ting i baggrunden med god brug af promises og callbacks.

    Nedenfor er et modificeret kodeeksempel med en forklaring taget fra min open source currency-api 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 };
    }

    Hvis du ser nærmere efter, er det uskyldigt udseende db.query på linje 3, blevet skubbet i baggrunden. Så det vil udføre som nedenfor:

    1. Hent kurs
    2. Kør indsæt forespørgsel i baggrunden
    3. Mens indsætningen kører funktionen er allerede returneret kursen
    4. Hvis der er et problem i indsæt forespørgslen er det logget i catch

    Der er ingen out of the box måde at gøre noget som dette i PHP. Dette er den første ting, der stumper PHP-udviklere. Det gør det sværere at forstå Node.js for PHP-udviklere. Denne asynkrone kodeudførelsesadfærd gør det også sværere at finde den rigtige stack trace i tilfælde af fejl i Node.js.

    For at være ærlig kan man i 2020 sagtens bruge asynkron await. Selv om det er syntaktisk sukker på Promises, gør det asynkron programmering en helvedes masse nemmere. Da jeg startede i Node 4/6-æraen omkring 2016 med callbacks og Promises var det et helt andet boldspil. Alligevel skal man være opmærksom på hvornår man ikke skal bruge async-await (som ovenfor) og bare gå med promises, then og catch. Lad dog være med at blive viklet ind i promise hell i processen. Promise hell er som den næste iteration af callback-helvede.

    Pro tip: Hvis du vil vide, hvilke ES6-funktioner du kan bruge med hvilken version af Node.js, kan du tjekke det på node.green.

    Endnu et pro tip:

    Endnu et pro tip:

    Eganske Node.js versioner er LTS, ulige versioner er det ikke. Så brug Node 14 eller 16 ikke 13 eller 15 i produktionen.

    Går vi lidt dybere ind i ikke-sekventiel udførelse, spiller promises og den kraft, det har, en vigtig rolle her. Muligheden for at gøre samtidige ting er fantastisk i Node.js og javascript generelt.

    Node.js promises muligheder #

    Promises er asynkrone, så du kan køre dem samtidig. Der er måder at gøre det på. Du kunne køre 3 promises i løb og få resultaterne fra den hurtigste. Du kan endda gøre promise.all, hvor hvis et løfte afvises, stopper det hele operationen. Læs mere om Promise.race, promise.all og promise.any i denne store sammenligning.

    Med det i tankerne kan du prøve andre NPM-biblioteker for at begrænse løftekonkurrencen eller endda filtrere gennem løfter samtidig. Du kan gøre noget af det med ReactPHP. Men det er ikke inkluderet i den native PHP, ikke engang i PHP 8. Det er noget nyt at pakke hovedet ind i Node.js for PHP-udviklere.

    Lad os gå videre til det næste punkt, processen behøver ikke at dø i Node.js som i PHP.

    Node.js-processen er langvarig, i modsætning til PHP #

    PHP skal dø ikke i den forstand, at den ikke bliver brugt. I den forstand, at alle PHP-processer skal dø. PHP er ikke rigtig designet til langkørende opgaver/processer. I PHP når en ny HTTP request kommer ind starter behandlingen, efter at have sendt svaret tilbage bliver processen dræbt. Det er sådan, PHP fungerer. Det skaber behovet for FPM og andre servere. Man kan argumentere for at PHP var serverless by design for 20+ år siden. Det lader jeg være op til dig.

    På den anden side er Node.js en langvarig proces. Dette giver dig mulighed for at dele oplysninger mellem anmodninger, da den samme server/proces håndterer flere anmodninger. Med en long-running process kan du nemt udnytte ting som memoization på hukommelse og connection pooling for en database. Det åbner op for andre muligheder som f.eks. at tælle antallet af samtidige forespørgsler på den pågældende proces.

    Memoization eksempel #

    Hvis du ikke kender Memoization.

    Memoization er en funktion af højere orden, der cacher en anden funktion. Den kan forvandle nogle langsomme funktioner til hurtige funktioner. Den gemmer resultatet af et funktionskald efter første gang til cachen, så hvis du kalder funktionen igen med de samme argumenter, vil den finde den i cachen.

    Den kan bruges i Node.js, men ikke i PHP nativt. Nogle workaround er mulig i PHP som at gemme funktionens returværdi i Redis.

    Nedenfor er et kodeeksempel på memoisering på en express-rute med 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);
    }
    });

    Den klare fordel ved dette er mindre belastning på datastore. I 1 minut vil den svare tilbage med det samme svar for de samme parametre. Output af funktionen products.getMultiple lagres i hukommelsen i et minut i cachen. Dette gør svarene meget hurtige.

    Connection Pool-eksempel med MySQL #

    En anden ting, der ikke er mulig på grund af en døende proces i PHP, er connection pooling. Ifølge Wikipedia:

    I software engineering er en connection pool en cache af databaseforbindelser, der vedligeholdes, så forbindelserne kan genbruges, når fremtidige anmodninger til databasen er nødvendige. Forbindelsespools bruges til at forbedre ydeevnen ved udførelse af kommandoer på en database.

    Så du har 5 forbindelser i en pool, og hvis du vil køre 5 forespørgsler til databasen, kan det gøres samtidig. Dette sparer tid både til at oprette forbindelse til databasen og til at køre forespørgslen. Dette er let at gøre i Node.js, men ikke let muligt i PHP.

    Vær opmærksom på antallet af tilgængelige forbindelser og på at holde din forbindelsespuljestørrelse optimal.

    Til eksempel, hvis du bruger Kubernetes, og din applikation har 5 pods med en forbindelsespuljestørrelse på 2. Det betyder, at din database altid vil have 10 åbne forbindelser, selv om der ikke udføres nogen forespørgsler.

    Tid for et eksempel på en forbindelsespulje med MySQL-database med MySQL npm-modul:

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

    Overstående kode vil køre den samme forespørgsel 5 gange parallelt med 5 MySQL-forbindelser taget fra forbindelsespuljen. Jeg ville ønske, at jeg kunne gøre dette i PHP out of the box.

    Med min erfaring fungerer Node.js meget godt sammen med Mysql. Hvis du vil prøve connection pooling med Mongo DB, er her et Mongo-eksempel.

    Med en langvarig proces skal man som udvikler være mere opmærksom på hukommelseslækager og gøre de husholdningsmæssige ting godt.

    Det er her, Node.js for PHP-udviklere har brug for en hel del skift i tankegangen om, hvordan koden udføres. På den anden side er dette en stor fordel i Node.js for PHP-udviklere.

    Debugging er nemmere i Node.js end i PHP #

    Debugging af kode linje for linje er en vigtig del af udviklerens oplevelse for ethvert programmeringssprog. For at debugge PHP-kode kan du bruge add ons som X-Debug med nogle IDE-indstillinger. X-Debug er mildest talt udfordrende at sætte op. Du er nødt til at installere det, aktivere udvidelsen. Derefter skal du konfigurere det korrekt med et IDE som PHPStorm.

    Grundlæggende er let det sidste, du vil sige om at få X-debug til at køre. Medmindre det hele er konfigureret godt med en docker container og IDE-indstillingerne er også nemme at indlæse.

    På den anden side er det meget nemmere at køre node native debugger eller endda ndb sammenlignet med PHP og X-debug. Med brug af VS Code er det så nemt at debugge Node.js-applikation, at selv en hulemand kan gøre det.

    Åbn Præferencer > Indstillinger, og i søgefeltet skal du skrive “node debug”. Under fanen Udvidelser bør der være en udvidelse med titlen “Node debug”. Herfra skal du klikke på det første felt: Debug > Node: Auto Attach og indstil rullemenuen til “on”. Du er næsten klar til at gå i gang nu. Ja, det er virkelig så nemt.

    Sæt derefter nogle breakpoints på VS-koden med f.eks. index.js og skriv node --inspect index.js i terminalen.

    BOOM! Din trinvise Node.js-debugger kører godt på VS Code-editoren uden større anstrengelser. En god forskel fra PHP er, at der ikke er behov for at installere en anden udvidelse, aktivere den og konfigurere den for at kunne debugge et program. Intet behov for at installere en ekstra udvidelse er en fordel, der findes i Node.js for PHP-udviklere.

    Det næste punkt handler også om bedre udvikleroplevelse ved opgradering af selv flere større versioner af sproget.

    Opgradering af større versioner i Node.js er problemfri i forhold til PHP #

    Springning af selv flere større versioner i Node.js er en problemfri oplevelse. Opgradering fra PHP 5.x til PHP 7.x er en proces, der tager en uge til en måned, afhængigt af projektets størrelse og kompleksitet.

    I min personlige erfaring har jeg tidligere opgraderet Node.js-microservices fra version 0.12 til 4. For nylig opgraderede jeg en applikation fra Node.js 10 til 14. Alle mine opgraderinger af større Node.js-versioner har været nemme.

    Nogle mindre package.json-ændringer var de eneste små problemer, jeg stødte på. Efter udrulning var der sjældent nogen problemer i forbindelse med kodekompatibilitet. Som en ekstra bonus var ydeevnen normalt bedre ved opgradering af de større versioner.

    På den anden side har det ikke været let at opgradere PHP. Opgradering af mindre versioner for en applikation fra PHP 5.4 til 5.6 var ikke særlig besværlig. Men at gå fra PHP 5.6 til 7.2 for en relativt stor applikation var en smerte. Det tog lang tid, og det krævede flere composer.json-ændringer. Det var også en vanskelig opgave at teste det. Den gode side af en større versionsopgradering i PHP var helt sikkert ydelsesforbedringen.

    Det skal lige bemærkes her, at de PHP-applikationer, jeg arbejdede med, var ældre end Node.js-applikationerne. Din oplevelse kan helt sikkert være anderledes end min.

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

    Docker’s popularitet har været støt stigende i de sidste 5 år. Det har ændret den måde, som vi softwareingeniører arbejder på, siden det blev udgivet. Du bør også bruge Docker til lokal udvikling. Med det in mente kan Dockerizing af en PHP-applikation være en vanskelig opgave, afhængigt af hvordan komponenterne er lagt ud, og hvor kompleks applikationen er. Omvendt er indsatsen mindre for dockerizing af en Node.js-applikation, og processen er en leg.

    Nedenfor er et eksempel på en dockerfil til en PHP Laravel-app med 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

    Det gode ved dette Docker-image til Laravel er, at PHP er bundlet med apache i det samme image. Man kan argumentere for, om det er en bedre måde at gøre det på end at opdele PHP og Apache i to docker-images.

    Også bemærk docker-bygningen i flere faser i ovenstående docker-image. Composer-installationen udføres i et andet image, og output kopieres til det primære image. Hvis vi havde brugt PHP-FPM og Nginx i forskellige docker-images, ville det have været mere komplekst. Der ville være behov for at administrere to forskellige docker-images.

    Nu er det tid til at se på en Node.js Dockerfile.

    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

    Da Node.js har en indbygget webserver, er Dockerfilen meget renere.

    Når du installerer Node, er npm bundlet med. Dette eliminerer behovet for at installere pakker på et andet trin i docker-buildingen.

    I ovenstående Dockerfil bruges flertrins docker-building med flere trin til at adskille produktions- og udviklingsdocker-images. At pakkehåndteringen (npm) er bundtet og at webserveren er en del af sproget/runtime er noget andet i Node.js for PHP-udviklere. Hvis du er mere interesseret i at docke en Node.js applikation trin for trin, så følg denne tutorial.

    Konklusion #

    Når du bruger Node.js til PHP-udviklere, kræver det et mildt skift i tankegangen for at udnytte Node.js’ kræfter godt. Node.js er ikke en sølvkugle. Der er ulemper, og det kræver tilpasning til forskellige måder at udføre kode på.

    Sikkert er der nogle fordele ved at bruge Node.js til PHP-udviklere som asynkron programmering og samtidighed. Andre fordele udspringer af Node.js-processen er langvarig.

    Jeg håber, at dette indlæg hjælper dig som erfaren PHP-udvikler med at få mere ud af Node.js.

    Skriv et svar

    Din e-mailadresse vil ikke blive publiceret.