Node.js PHP-kehittäjille: 5 must-know käytännön näkökohtaa koodiesimerkkien kera

Node.js:n suosio kasvaa, mutta PHP:n vetovoima laskee. Tätä taustaa vasten tässä postauksessa käsitellään tarkemmin 5 pakollista käytännön näkökohtaa Node.js:n käytöstä PHP-kehittäjille. Nämä tulevat olemaan asioita, joista kukaan ei puhu tai kirjoita, aika lähteä liikkeelle.

  1. Node.js PHP-kehittäjille (ei Node.js vs PHP)
  2. Node.js PHP-kehittäjille käytännöllinen puoli
    1. Node.js-koodin suoritus on asynkronista ja ei-sekventiaalista
      1. Node.js lupaa mahdollisuuksia
    2. Node.js-prosessi on pitkäkestoinen, toisin kuin PHP:ssä
      1. Muistiinpanoesimerkki
      2. Yhteyspooli-esimerkki MySQL:llä
    3. Debuggaus on helpompaa Node.js:ssä kuin PHP:ssä
    4. Node.js:ssä versiopäivitykset ovat saumattomia PHP:hen verrattuna
    5. Node.js-sovelluksen dokkarointi on lastenleikkiä PHP:hen verrattuna
  3. Johtopäätös

Node.js PHP-kehittäjille (ei Node.js vs PHP) #

Tämä kappale on lista asioista, jotka sinun PHP-kehittäjänä on tiedettävä ja opittava, jotta voit käyttää Node.js:ää tehokkaasti. Päinvastoin, tämä postaus ei ole Node.js vs PHP -kirjoitus, jossa PHP:tä haukutaan. Olen käyttänyt molempia kieliä. Aloin kirjoittaa enemmän Node.js:ää vuonna 2016. Aloittaessani kohtasin joitakin vaikeuksia, koska olin tottunut PHP:hen työssä yli 7 vuotta sitä ennen. Vuoden 2012 loppupuolella julkaistiin kirja, joka käsitteli Node.js:ää PHP-kehittäjille.

Tässä blogikirjoituksessa ei puhuta siitä, mitä PHP tai Node.js on, voit lukea siitä muista kirjoituksista. En myöskään puhu paljon Non-Blocking I/O:sta tai tapahtumasilmukasta. Silti osa niistä sivutaan läpi, kun keskustellaan hyvän Node.js-koodin kirjoittamisen käytännön puolista.

Node.js PHP-kehittäjille käytännön puoli #

PHP on ollut elossa vuodesta 1995 lähtien ja tiettävästi sitä käyttää edelleen 79.% W3techin valvomista verkkosivuista (en osaa sanoa, onko se koko internet). On siis hyvin todennäköistä, että olet käyttänyt PHP:tä tai ottanut käyttöön jotain PHP:llä kirjoitettua. Esimerkiksi kasvavalla trendillä:

WordPressiä käyttää 63,7 % kaikista sivustoista, joiden sisällönhallintajärjestelmän tunnemme. Tämä on 39,0 % kaikista W3Techin seuraamista verkkosivustoista.

Toisaalta Node.js julkaistiin vuonna 2009. Suuret teknologiayritykset, kuten Linked In ja Paypal, alkoivat ottaa sitä käyttöön vuosina 2011-2013 eri syistä, kuten mikropalveluiden vuoksi. Stack Overflow -kehittäjätutkimuksen mukaan vuodelta 2020:

Toisena vuonna peräkkäin Node.js vie kärkipaikan, sillä sitä käyttää puolet vastaajista.

Ei ole mikään salaisuus, että Node.js:n suosio on noussut erittäin suureksi viimeisen viiden vuoden aikana.

PHP-kehittäjänä nämä ovat siis 5 käytännön asiaa, jotka sinun on pakko osata, jotta voit menestyä loistavana Node.js:n ohjelmistoteknikkona. Node.js PHP-kehittäjille on jossain mielessä samanlainen, mutta myös erilainen joissakin muissa asioissa, joista osa on kuvattu alla:

Node.js-koodin suoritus on asynkkä ja ei-sekventiaalinen #

Tämä käyttäytyminen huijaa monia PHP-kehittäjiä. PHP:ssä koodi suoritetaan peräkkäin, ensin rivi 1 sitten 2 ja niin edelleen. Javascriptissä ja erityisesti Node.js:ssä näin ei välttämättä ole. Voit potentiaalisesti laittaa asioita taustalle lupausten ja callbackien hyvällä käytöllä.

Alhaalla on muokattu koodiesimerkki selityksineen, joka on otettu avoimen lähdekoodin currency-api-repostani:

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

Jos katsot tarkemmin, tuo viattoman näköinen db.query rivillä 3, on työnnetty taustalle. Eli se suoritetaan kuten alla:

  1. Hakee kurssin
  2. Johtaa insert-kyselyä taustalla
  3. Kun insert on käynnissä, funktio on jo palauttanut kurssin
  4. Jos insert-kyselyssä on ongelma, se kirjautuu catchiin

Php:ssä ei ole mitään out of the box tapaa, jolla voisi PHP:ssä tehdä jotakin vastaavaa. Tämä on ensimmäinen asia, joka tyrmää PHP-kehittäjät. Se vaikeuttaa Node.js:n ymmärtämistä PHP-kehittäjille. Tämä asynkroninen koodin suorituskäyttäytyminen tekee myös oikean pinojäljen löytämisen vaikeammaksi virheiden sattuessa Node.js:ssä.

Olen rehellinen, vuonna 2020 voit helposti käyttää asynkronista awaitia. Vaikka se on Promisesin syntaktista sokeria, se tekee asynkronisesta ohjelmoinnista helvetin paljon helpompaa. Kun aloitin Node 4/6 aikakaudella noin vuonna 2016 callbackien ja Promisesien kanssa se oli ihan eri asia. Silti kannattaa varoa, milloin ei kannata käyttää async-awaitia (kuten edellä) ja tyytyä Promisesiin, theniin ja catchiin. Älä kuitenkaan sotkeudu lupaushelvettiin prosessin aikana. Promise-helvetti on kuin callback-helvetin seuraava iteraatio.

Pro-vinkki: Jos haluat tietää, mitä ES6-ominaisuuksia voit käyttää milläkin Node.js:n versiolla, tutustu siihen osoitteessa node.green.

Toinen pro-vinkki:

Jopa Node.js:n versiot ovat LTS-versioita, parittomat eivät. Käytä siis tuotannossa Nodea 14 tai 16, älä 13 tai 15.

Hieman syvemmälle ei-sekventiaaliseen suoritukseen, lupaukset ja niiden voima on tärkeässä roolissa. Kyky tehdä samanaikaisia asioita on loistava Node.js:ssä ja javascriptissä yleensä.

Node.js:n lupausten mahdollisuudet #

Lupausten ollessa asynkronisia, voit suorittaa niitä yhtäaikaisesti. Siihen on olemassa tapoja. Voit kilpailuttaa 3 lupausta ja saada tulokset nopeimmasta. Voit jopa tehdä promise.all jossa jos yksi lupaus hylätään, se pysäyttää koko operaation. Lue lisää Promise.race:stä, promise.all:stä ja promise.any:stä tästä loistavasta vertailusta.

Tässä mielessä voit kokeilla muitakin NPM-kirjastoja, joilla voit rajoittaa lupausten samanaikaisuutta tai jopa suodattaa lupauksia yhtäaikaisesti. ReactPHP:llä voi tehdä jonkin verran sellaista. Mutta se ei sisälly natiiviin PHP:hen, ei edes PHP 8:aan. Tämä on PHP-kehittäjille jotain uutta käärittävää Node.js:ssä.

Siirrymme seuraavaan kohtaan, prosessin ei tarvitse kuolla Node.js:ssä kuten PHP:ssä.

Node.js:n prosessi on pitkäkestoinen, toisin kuin PHP:ssä #

PHP:ssä prosessin on tarkoitus kuolla ei siinä mielessä, että sitä ei käytetä. Siinä mielessä, että kaikkien PHP-prosessien on kuoltava. PHP:tä ei oikeastaan ole suunniteltu pitkäkestoisia tehtäviä/prosesseja varten. PHP:ssä uuden HTTP-pyynnön tullessa käsittely alkaa, vastauksen lähettämisen jälkeen prosessi tapetaan. Näin PHP toimii. Tämä luo tarpeen FPM:lle ja muille palvelimille. Voit väittää, että PHP oli palvelimeton jo 20+ vuotta sitten. Jätän sen sinun päätettäväksi.

Toisaalta Node.js on pitkäkestoinen prosessi. Tämä mahdollistaa tietojen jakamisen pyyntöjen välillä, kun sama palvelin/prosessi käsittelee useita pyyntöjä. Pitkään juoksevan prosessin avulla voit helposti hyödyntää asioita kuten muistin muistinmuokkausta ja tietokannan yhteyksien yhdistämistä (connection pooling). Se avaa muitakin mahdollisuuksia, kuten esimerkiksi kyseisen prosessin samanaikaisten pyyntöjen lukumäärän laskemisen.

Esimerkki muistinmuokkauksesta #

Jos et tunne muistinmuokkausta.

Esimerkki muistinmuokkauksesta on ylemmän asteen funktio, joka kätkee toisen funktion. Se voi muuttaa jotkut hitaat funktiot nopeiksi. Se tallentaa funktiokutsun tuloksen ensimmäisen kerran jälkeen välimuistiin, joten jos kutsut funktiota uudelleen samoilla argumenteilla, se löytää sen välimuistista.

Sitä voi käyttää Node.js:ssä, mutta ei PHP:ssa natiivisti. Jokin kiertotie on mahdollinen PHP:ssä, kuten funktion paluuarvon tallentaminen Redikseen.

Alhaalla on koodinäyte memoisaatiosta express-reitillä 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);
}
});

Tämän selkeä etu on pienempi kuormitus tietovarastolle. Yhden minuutin ajan se vastaa takaisin samalla vastauksella samoille parametreille. Funktio products.getMultiple:n ulostulo on välimuistissa minuutin ajan. Tämä tekee vastauksista erittäin nopeita.

Connection Pool esimerkki MySQL:llä #

Toinen asia, joka ei ole mahdollista PHP:n kuolevan prosessin takia, on connection pooling. Wikipedia:

Wikipedian mukaan:

Ohjelmistotekniikassa yhteyspooli on tietokantayhteyksien välimuisti, jota ylläpidetään niin, että yhteyksiä voidaan käyttää uudelleen, kun tietokantaan kohdistuvia pyyntöjä tarvitaan tulevaisuudessa. Yhteyspooleja käytetään parantamaan tietokannan komentojen suorittamisen suorituskykyä.

Yhteyspooleissa on siis 5 yhteyttä, ja jos haluat suorittaa 5 kyselyä tietokantaan, se voidaan tehdä yhtäaikaisesti. Tämä säästää aikaa sekä tietokantaan yhdistämiseen että kyselyn suorittamiseen. Tämä on helppo tehdä Node.js:ssä, mutta se ei ole helposti mahdollista PHP:ssä.

Ole tarkkana käytettävissä olevien yhteyksien määrän suhteen ja pidä yhteyspoolien koko optimaalisena.

Jos esimerkiksi käytät Kubernetesia ja sovelluksessasi on 5 podia, joiden yhteyspoolien koko on 2. Jos esim. Se tarkoittaa, että tietokannassasi on aina 10 avointa yhteyttä, vaikka kyselyjä ei suoriteta.

Aika esimerkki yhteyspoolista MySQL-tietokannalla MySQL npm-moduulin avulla:

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

Yllä oleva koodi suorittaa saman kyselyn 5 kertaa rinnakkain 5:llä yhteyspoolista otetulla MySQL-yhteydellä. Toivoin, että voisin tehdä tämän PHP:llä out of the box.

Kokemukseni mukaan Node.js toimii erittäin hyvin Mysql:n kanssa. Jos haluat kokeilla connection poolingia Mongo DB:n kanssa, tässä on Mongo-esimerkki.

Kehittäjänä pitkäkestoisen prosessin kanssa täytyy olla tarkempi muistivuodoista ja tehdä taloudenhoitoasiat hyvin.

Tässä kohtaa Node.js for PHP -kehittäjät tarvitsevat melkoista muutosta ajattelussa siitä, miten koodia suoritetaan. Toisaalta tämä on suuri etu Node.js for PHP -kehittäjille.

Debuggaaminen on helpompaa Node.js:ssä kuin PHP:ssä #

Rivikoodin rivikohtainen debuggaaminen on tärkeä osa kehittäjäkokemusta missä tahansa ohjelmointikielessä. PHP-koodin debuggaamiseen voi käyttää lisäosia, kuten X-Debugia, joidenkin IDE-asetusten kanssa. X-Debug on lievästi sanottuna haastava asentaa. Sinun on asennettava se, otettava laajennus käyttöön. Sen jälkeen konfiguroida se kunnolla PHPStormin kaltaisen IDE:n kanssa.

Periaatteessa helppo on viimeinen asia, jota sanotaan X-debugin käyttöönotosta. Paitsi jos kaikki on konfiguroitu hyvin docker containerilla ja IDE:n asetukset on myös helppo ladata.

Toisaalta node native debuggerin tai jopa ndb:n ajaminen on paljon helpompaa verrattuna PHP:hen ja X-debugiin. VS Koodin avulla Node.js-sovelluksen debuggaaminen on niin helppoa, että jopa luolamies pystyy siihen.

Avaa Asetukset > Asetukset ja kirjoita hakukenttään ”node debug”. Laajennukset-välilehdellä pitäisi olla yksi laajennus nimeltään ”Node debug”. Klikkaa täältä ensimmäistä ruutua: Debug > Node: Auto Attach ja aseta pudotusvalikko kohtaan ”on”. Olet nyt melkein valmis. Kyllä, se on todella niin helppoa.

Sitten aseta VS-koodiin breakpointit vaikkapa index.js ja kirjoita terminaaliin node --inspect index.js.

BOOM! Vaiheittainen Node.js-debuggerisi toimii hyvin VS Code -editorissa ilman suurempaa vaivaa. Hyvä ero PHP:hen, ei tarvitse asentaa erilaista laajennusta, ottaa se käyttöön ja konfiguroida sitä, jotta voit debugata ohjelmaa. Ei tarvitse asentaa ylimääräistä laajennusta on PHP-kehittäjille Node.js:stä löytyvä etu.

Seuraava kohta koskee myös parempaa kehittäjäkokemusta päivitettäessä jopa useita pääversioita kielestä.

Major-versiopäivitykset Node.js:ssä ovat saumattomia PHP:hen nähden #

Jopa useiden pääversioiden hyppääminen Node.js:s:ssä on saumatonta toimintaa. PHP 5.x:stä PHP 7.x:ään päivittäminen on viikon tai kuukauden mittainen prosessi riippuen projektin koosta ja monimutkaisuudesta.

Oman henkilökohtaisen kokemukseni mukaan olen aiemmin päivittänyt Node.js:n mikropalveluja versioista 0.12 versioon 4. Äskettäin päivitin sovelluksen Node.js 10:stä 14:ään. Kaikki Node.js:n pääversiopäivitykseni ovat olleet helppoja.

Jotkut pienet package.json-muutokset olivat ainoat pienet ongelmat, joita kohtasin. Käyttöönoton jälkeen oli harvoin mitään koodin yhteensopivuuteen liittyviä ongelmia. Lisäbonuksena suorituskyky oli yleensä parempi päivittämällä pääversiot.

Toisaalta PHP:n päivittäminen ei ole ollut helppoa. Sovelluksen pienversiopäivitys PHP 5.4:stä 5.6:een ei ollut kovin hankala. Mutta suhteellisen suuren sovelluksen siirtyminen PHP 5.6:sta 7.2:een oli tuskaa. Se kesti kauan ja vaati useita composer.json-muutoksia. Sen testaaminen oli myös vaikeaa. PHP:n suuren versiopäivityksen hyvä puoli oli varmasti suorituskyvyn parantuminen.

Huomautuksena mainittakoon, että PHP-sovellukset, joiden kanssa työskentelin, olivat vanhempia kuin Node.js-sovellukset. Kokemuksesi voi varmasti olla erilainen kuin omani.

Node.js-sovelluksen dokkarointi on helppoa PHP:hen verrattuna #

Dokkarin suosio on kasvanut tasaisesti viimeisen viiden vuoden aikana. Se on muuttanut meidän ohjelmistosuunnittelijoiden työskentelytapaa sen julkaisun jälkeen. Dockeria kannattaa käyttää myös paikalliseen kehitykseen. Tätä silmällä pitäen PHP-sovelluksen Dockerointi voi olla vaikea tehtävä riippuen siitä, miten komponentit on sijoitettu ja kuinka monimutkainen sovellus on. Sitä vastoin Node.js-sovelluksen dockerisoinnissa vaiva on vähäisempi ja prosessi on lastenleikkiä.

Alhaalla on esimerkki docker-tiedostosta PHP-Laravel-sovellukselle, jossa on 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

Hyvä puoli tässä Docker-kuvauksessa Laravelille on se, että PHP on niputettu apachen kanssa samaan kuvaan. Voidaan kiistellä, onko tämä parempi tapa kuin PHP:n ja Apachen jakaminen kahteen docker-imageen.

Huomaa myös monivaiheinen docker-build yllä olevassa docker-imagessa. Composerin asennus tehdään eri imagessa ja tuotos kopioidaan pää-imageen. Jos olisimme käyttäneet PHP-FPM:ää ja Nginxiä eri docker-imageissa, se olisi ollut monimutkaisempaa. Olisi pitänyt hallita kahta erillistä docker-imagea.

Nyt on aika katsoa Node.js:n 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

Koska Node.js:ssä on sisäänrakennettu web-palvelin, dockerfile on paljon siistimpi.

Kun asennat nodea, npm on niputettu sen mukana. Tämä poistaa tarpeen asentaa paketteja eri vaiheessa docker buildissa.

Yllä olevassa dockerfileessä käytetään monivaiheista docker buildia erottamaan tuotanto- ja kehitystyön docker-kuvat toisistaan. Paketinhallinnan (npm) niputtaminen ja web-palvelimen ottaminen osaksi kieltä/runtimea on jotain erilaista Node.js:ssä PHP-kehittäjille. Jos sinua kiinnostaa enemmän Node.js-sovelluksen dokkarointi askel askeleelta, seuraa tätä opetusohjelmaa.

Johtopäätös #

Käytettäessä Node.js:ää PHP-kehittäjille tarvitaan lievää ajattelutavan muutosta, jotta Node.js:n tehoja voidaan hyödyntää hyvin. Node.js ei ole mikään hopealuoti. Siinä on haittapuolia, ja se vaatii sopeutumista erilaisiin koodin suoritustapoihin.

Node.js:n käytöstä PHP-kehittäjille on toki joitain hyötyjä, kuten asynkinen ohjelmointi ja samanaikaisuus. Muut edut kumpuavat Node.js-prosessi on pitkäkestoinen.

Toivottavasti tämä viesti auttaa sinua kokeneena PHP-kehittäjänä saamaan enemmän irti Node.js:stä.

Vastaa

Sähköpostiosoitettasi ei julkaista.