Míg a Node.js népszerűsége növekszik, a PHP vonzereje csökken. Ebben a kontextusban ez a bejegyzés a Node.js használatának 5 kötelezően megismerendő gyakorlati szempontját dolgozza fel a PHP-fejlesztők számára. Ezek olyan dolgok lesznek, amikről senki sem beszél vagy ír, ideje belevágni.
- Node.js PHP fejlesztőknek (nem Node.js vs PHP)
- Node.js PHP fejlesztőknek a gyakorlati oldal
- A Node.js kódfuttatás aszinkron és nem szekvenciális
- Node.js lehetőségeket ígér
- A Node.js folyamat hosszú futású, ellentétben a PHP-vel
- Memoizációs példa
- Connection Pool példa MySQL-el
- A hibakeresés könnyebb a Node.js-ben, mint PHP-ben
- A nagyobb verziófrissítések a Node.js-ben zökkenőmentesek a PHP-vel szemben
- A Node.js alkalmazás dokkolása gyerekjáték a PHP-hoz képest
- A Node.js kódfuttatás aszinkron és nem szekvenciális
- Következtetés
Node.js PHP fejlesztőknek (nem Node.js vs PHP) #
Ez a darab egy lista azokról a dolgokról, amelyeket PHP fejlesztőként tudnod és tanulnod kell ahhoz, hogy hatékonyan használhasd a Node.js-t. Ezzel szemben ez a bejegyzés nem egy Node.js vs PHP írás, ahol a PHP-t baszogatják. Mindkét nyelvet használtam. Több Node.js-t 2016-ban kezdtem el írni. Amikor elkezdtem, némi nehézséggel szembesültem, mivel előtte több mint 7 évig a PHP-t használtam a munkahelyemen. 2012 vége felé megjelent egy könyv, amely a Node.js-t tárgyalja a PHP-fejlesztők számára.
Ez a blogbejegyzés nem fog arról beszélni, hogy mi a PHP vagy a Node.js, erről más bejegyzésekben olvashat. A Non-Blocking I/O-ról vagy az eseményhurokról sem fogok sokat beszélni. Mégis, ezek egy részét átfésüljük, amikor a jó Node.js kód írásának gyakorlati szempontjait tárgyaljuk.
Node.js PHP fejlesztőknek a gyakorlati oldal #
A PHP 1995 óta él, és állítólag még mindig a W3tech által megfigyelt weboldalak 79,%-a használja (azt nem igazán tudom megmondani, hogy az egész internet). Tehát nagyon nagy az esélye annak, hogy használtál már PHP-t vagy telepítettél valamit, ami PHP-ben íródott. Például növekvő tendenciával:
A WordPress-t az összes olyan weboldal 63,7%-a használja, amelynek tartalomkezelő rendszerét ismerjük. Ez a W3Tech által megfigyelt összes weboldal 39,0%-a.
A Node.js viszont 2009-ben jelent meg. Az olyan nagy technológiai vállalatok, mint a Linked In és a Paypal 2011 és 2013 között kezdték el alkalmazni különböző okokból, például a mikroszolgáltatások miatt. A Stack Overflow 2020-as fejlesztői felmérése szerint:
A második egymást követő évben a Node.js foglalja el az első helyet, mivel a válaszadók fele használja.
Nem titok, hogy a Node.js az elmúlt 5 évben nagyon népszerűvé vált.
Az alábbi 5 gyakorlati dolgot kell tudnod PHP-fejlesztőként, hogy nagyszerű Node.js szoftverfejlesztő legyél. A Node.js a PHP-fejlesztők számára bizonyos értelemben hasonló, de néhány más szempontból különbözik is, amelyek közül néhányat az alábbiakban ismertetünk:
A Node.js kódfuttatása aszinkron és nem szekvenciális #
Ez egy olyan viselkedés, amely sok PHP-fejlesztőt becsap. A PHP-ben a kód sorrendben fut, először az 1. sor, majd a 2. sor, és így tovább. A Javascriptben és különösen a Node.js-ben ez nem biztos, hogy így van. Az ígéretek és visszahívások jó használatával potenciálisan háttérbe helyezhetsz dolgokat.
Az alábbiakban egy módosított kódpélda magyarázattal a nyílt forráskódú currency-api repómból:
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 };
}
Ha jobban megnézed, az az ártatlannak tűnő db.query
a 3. sorban, a háttérbe lett tolva. Tehát az alábbiak szerint fog lefutni:
- Kérdezze az árfolyamot
- Futtasson beillesztési lekérdezést a háttérben
- Míg a beillesztés fut a függvény már visszaadta az árfolyamot
- Ha a beillesztési lekérdezésben probléma van, akkor azt a catch
Nincs out of the box módja, hogy ilyesmit PHP-ben csináljunk. Ez az első dolog, ami megakasztja a PHP fejlesztőket. Ez megnehezíti a Node.js megértését a PHP fejlesztők számára. Ez az aszinkron kódvégrehajtási viselkedés megnehezíti a megfelelő stack trace megtalálását is hiba esetén a Node.js-ben.
Az igazat megvallva, 2020-ban könnyen használhatsz async await-ot. Bár ez egy szintaktikai cukor a Promises-on, de pokolian megkönnyíti az aszinkron programozást. Amikor 2016 körül kezdtem a Node 4/6 korszakban callbackekkel és Promises-okkal, az egy teljesen más labdajáték volt. Mégis, vigyázz, mikor ne használd az async-await-ot (mint fentebb), és csak a Promises-t, akkor és catch-t használd. Azért ne keveredj bele az ígéretek poklába közben. A promise hell olyan, mint a callback hell következő iterációja.
Protipp: Ha tudni szeretnéd, hogy a Node.js melyik verziójával milyen ES6 funkciókat használhatsz, nézz utána a node.green.
Még egy protipp:
A Node.js egyes verziói LTS, a páratlanok nem. Tehát a Node 14-et vagy 16-ot használd, ne a 13-at vagy 15-öt a termelésben.
Kicsit mélyebben belemerülve a nem szekvenciális végrehajtásba, a promises és annak ereje fontos szerepet játszik itt. A Node.js-ben és általában a javascriptben nagyszerű az egyidejű dolgok végrehajtásának képessége.
Node.js ígéretek lehetőségei #
Az ígéretek aszinkron lévén, párhuzamosan is futtathatjuk őket. Erre is vannak módszerek. Versenyeztethetsz 3 ígéretet, és az eredményt a leggyorsabbtól kapod meg. Még azt is megteheted promise.all
, hogy ha egy ígéret elutasításra kerül, akkor leáll az egész művelet. Olvasson többet a Promise.race
, promise.all
és promise.any
lehetőségekről ebben a nagyszerű összehasonlításban.
Ezt szem előtt tartva kipróbálhat más NPM könyvtárakat is az ígéretek egyidejűségének korlátozására, vagy akár az ígéretek egyidejű szűrésére. A ReactPHP-vel is megtehetsz valamennyit. De ez nincs benne a natív PHP-ben, még a PHP 8-ban sem. Ez valami újdonság a Node.js-ben a PHP-fejlesztők számára, amire rá kell tekerni a fejüket.
Lépjünk tovább a következő pontra, a folyamatnak nem kell meghalnia a Node.js-ben, mint a PHP-ben.
A Node.js folyamat hosszú futású, ellentétben a PHP-vel #
A PHPP-ben nem abban az értelemben kell meghalnia, hogy nem fogják használni. Abban az értelemben, hogy minden PHP folyamatnak meg kell halnia. A PHP-t nem igazán hosszú futású feladatokra/folyamatokra tervezték. A PHP-ben amikor egy új HTTP kérés érkezik a feldolgozás elindul, a válasz visszaküldése után a folyamatot megöli. Így működik a PHP. Ez teremti meg az FPM és más szerverek szükségességét. Érvelhetünk azzal, hogy a PHP már 20+ évvel ezelőtt is szervermentes volt. Ezt rád bízom.
A másik oldalon a Node.js egy hosszan futó folyamat. Ez lehetővé teszi az információk megosztását a kérések között, mivel ugyanaz a szerver/folyamat több kérést is kezel. Egy hosszú futású folyamattal könnyen kihasználhatsz olyan dolgokat, mint a memoizálás a memórián és a kapcsolatgyűjtés egy adatbázis számára. Ez olyan egyéb lehetőségeket nyit meg, mint például az adott folyamat egyidejű kéréseinek számolása.
Memoizáció példa #
Ha nem ismeri a memoizációt.
A memoizáció egy magasabb rendű függvény, amely egy másik függvényt gyorsítótárba helyez. Néhány lassú függvényt gyors függvényekké alakíthat. A függvényhívás eredményét az első alkalom után elmenti a gyorsítótárba, így ha újra meghívod a függvényt ugyanazokkal az argumentumokkal, akkor megtalálja a gyorsítótárban.
A Node.js-ben használható, de natívan a PHP-ben nem. Valamilyen megoldás lehetséges PHP-ben, például a függvény visszatérési értékének elmentése a Redisben.
Az alábbiakban egy kódminta a memoizálásról egy express route-on a p-memoize segítségével:
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);
}
});
Az egyértelmű előnye, hogy kevésbé terheli az adattárolót. Egy percig ugyanazzal a válasszal válaszol vissza ugyanazokra a paraméterekre. A products.getMultiple
függvény kimenete egy percig gyorsítótárazódik a memóriában. Ezáltal a válaszok nagyon gyorsak lesznek.
Connection Pool példa MySQL-el #
Egy másik dolog, ami a PHP-ben egy haldokló folyamat miatt nem lehetséges, az a connection pooling. A Wikipédia szerint:
A szoftvertechnikában a connection pool az adatbázis-kapcsolatok cache-je, amelyet azért tartanak fenn, hogy a kapcsolatok újra felhasználhatók legyenek, amikor a jövőben az adatbázishoz irányuló kérések szükségesek. A kapcsolati poolokat arra használják, hogy növeljék az adatbázisban végrehajtott parancsok teljesítményét.
Egy poolban tehát 5 kapcsolat lesz, és ha 5 lekérdezést akarunk futtatni az adatbázishoz, akkor azt párhuzamosan is megtehetjük. Ez időt takarít meg mind az adatbázishoz való csatlakozás, mind a lekérdezés futtatása során. Ez a Node.js-ben könnyen megvalósítható, de PHP-ben nem könnyű.
Figyeljen a rendelkezésre álló kapcsolatok számára, és tartsa a kapcsolati pool méretét optimálisnak.
Ha például Kubernetes-t használ, és az alkalmazásnak 5 podja van, a kapcsolati pool mérete pedig 2 db. Ez azt jelenti, hogy az adatbázisának mindig 10 nyitott kapcsolata lesz, még akkor is, ha nem hajtanak végre lekérdezéseket.
Egy kapcsolati pool példa MySQL adatbázissal, MySQL npm modullal:
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
});
}
A fenti kód ugyanazt a lekérdezést 5 alkalommal futtatja párhuzamosan 5 MySQL kapcsolattal, amelyeket a kapcsolati poolból vesz. Bárcsak ezt PHP-ben is meg tudnám csinálni out of the box.
A tapasztalatom szerint a Node.js nagyon jól működik a Mysql-lel. Ha ki akarod próbálni a connection poolingot Mongo DB-vel, itt van egy Mongo példa.
Egy hosszú futású folyamatnál fejlesztőként jobban oda kell figyelned a memóriaszivárgásokra és a házvezetési dolgok jó elvégzésére.
Ez az a pont, ahol a Node.js for PHP fejlesztőknek eléggé át kell gondolni a kód futtatásának módját. Másrészt ez egy nagy előnye a Node.js for PHP fejlesztőknek.
A hibakeresés könnyebb a Node.js-ben, mint a PHP-ben #
A kód soronkénti hibakeresése minden programozási nyelv esetében fontos része a fejlesztői tapasztalatnak. A PHP kód hibakereséséhez olyan kiegészítőket használhatsz, mint az X-Debug, néhány IDE beállítással. Az X-Debug beállítása enyhén szólva kihívást jelent. Telepíteni kell, engedélyezni a bővítményt. Ezután megfelelően konfigurálni egy IDE-vel, például a PHPStormmal.
Az X-debug futtatására alapvetően könnyű az utolsó dolog, amit mondani fogsz. Hacsak nincs minden jól konfigurálva egy docker konténerrel és az IDE beállításai is könnyen betöltődnek.
Másrészt a node native debugger vagy akár az ndb futtatása sokkal egyszerűbb a PHP és az X-debug-hoz képest. A VS Code használatával a Node.js alkalmazás hibakeresése olyan egyszerű, hogy még egy ősember is meg tudja csinálni.
Nyissa meg a Beállítások > Beállítások és a keresőmezőbe írja be a “node debug” szót. A Bővítmények fül alatt kell lennie egy bővítménynek “Node debug” címmel. Innen kattintson az első mezőre: Debug > Csomópont: Auto Attach, és állítsa a legördülő listát “on”-ra. Most már majdnem készen állsz. Igen, tényleg ilyen egyszerű.
Ezután állítsunk be néhány töréspontot a VS kódon mondjuk a index.js
-vel, és a terminálban írjuk be a node --inspect index.js
-t.
BOOM! A Node.js debuggered lépésről lépésre, különösebb erőfeszítés nélkül jól fut a VS kódszerkesztőn. Jó különbség a PHP-hoz képest, hogy nem kell egy másik bővítményt telepíteni, engedélyezni és konfigurálni ahhoz, hogy debugolni tudjon egy programot. Nem kell külön bővítményt telepíteni, ez a Node.js-ben található előny a PHP-fejlesztők számára.
A következő pont szintén a jobb fejlesztői élményről szól, miközben akár több főverzióra is frissíthetünk a nyelvből.
A főverziófrissítés a Node.js-ben zökkenőmentes a PHP-vel szemben #
A Node.js-ben akár több főverzióra is zökkenőmentes az ugrás. A PHP 5.x-ről a PHP 7.x-re való frissítés a projekt méretétől és összetettségétől függően egy héttől egy hónapig tartó folyamat.
Személyes tapasztalatom szerint a múltban 0.12-es verzióról 4-es verzióra frissítettem Node.js mikroszolgáltatásokat. Nemrég frissítettem egy alkalmazást Node.js 10-es verzióról 14-esre. Minden nagyobb Node.js verziófrissítésem könnyű volt.
Az egyetlen apró probléma, amivel találkoztam, néhány kisebb package.json módosítás volt. A telepítés után ritkán voltak kódkompatibilitással kapcsolatos problémák. További bónuszként a teljesítmény általában jobb volt a fő verziók frissítése során.
A PHP frissítése viszont nem volt egyszerű. Egy alkalmazás kisebb verziófrissítése a PHP 5.4-ről az 5.6-ra nem volt túl nehézkes. De a PHP 5.6-ról 7.2-re való átmenet egy viszonylag nagy alkalmazás esetében fájdalmas volt. Hosszú ideig tartott, és többszörös composer.json módosításra volt szükség. A tesztelés is nehéz feladat volt. A PHP nagyobb verziófrissítésének jó oldala minden bizonnyal a teljesítménynövekedés volt.
Csak egy megjegyzés: a PHP-alkalmazások, amelyekkel dolgoztam, régebbiek voltak, mint a Node.js alkalmazások. A te tapasztalatod biztosan más lehet, mint az enyém.
Dockerizálni egy Node.js alkalmazást gyerekjáték a PHP-hoz képest #
A Docker népszerűsége az elmúlt 5 évben folyamatosan nőtt. Megjelentetése óta megváltoztatta a szoftvermérnökök munkamódszerét. A Dockert helyi fejlesztésre is érdemes használni. Ezt szem előtt tartva egy PHP-alkalmazás Dockerizálása nehéz feladat lehet, attól függően, hogy a komponensek hogyan vannak elrendezve és mennyire összetett az alkalmazás. Ezzel szemben egy Node.js alkalmazás dokkolásához kisebb az erőfeszítés, és a folyamat gyerekjáték.
Az alábbiakban egy példa egy dockerfile-ra egy PHP Laravel alkalmazáshoz Apache-al.
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 jó ebben a Laravelhez készült Docker-képben az, hogy a PHP az apache-al együtt van csomagolva ugyanabban a képben. Lehet vitatkozni, hogy ez jobb megoldás-e, mint a PHP és az Apache felosztása két docker image-re.
Azt is észrevehetjük, hogy a fenti docker image-ben többlépcsős a docker build. A Composer telepítése egy másik image-ben történik, és a kimenet átmásolódik a fő image-be. Ha a PHP-FPM-et és az Nginxet különböző docker image-ekben használtuk volna, akkor bonyolultabb lett volna a dolog. Két különböző docker image-t kellett volna kezelni.
Most itt az ideje, hogy megnézzünk egy Node.js Dockerfile-t.
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
Mivel a Node.js rendelkezik beépített webszerverrel, a Dockerfile sokkal tisztább.
Mikor a node-ot telepítjük, az npm csomagban van vele. Ez kiküszöböli a csomagok telepítésének szükségességét a docker build egy másik szakaszában.
A fenti Dockerfile-ban többlépcsős docker build-et használunk a termelési és a fejlesztési docker image-ek elkülönítésére. Az, hogy a csomagkezelő (npm) csomagolva van, és a webszerver a nyelv/runtime részeként van, a Node.js-ben a PHP fejlesztők számára valami más. Ha jobban érdekli egy Node.js alkalmazás dokkolása lépésről lépésre, kövesse ezt a bemutatót.
Végkövetkeztetés #
A Node.js PHP-fejlesztők számára történő használata során szükség van egy enyhe gondolkodásbeli váltásra, hogy a Node.js erőit jól ki lehessen használni. A Node.js nem egy csodafegyver. Vannak hátrányai, és alkalmazkodni kell a kódvégrehajtás különböző módjaihoz.
Kétségtelen, hogy a Node.js PHP-fejlesztők számára történő használatának vannak előnyei, mint például az aszinkron programozás és az egyidejűség. Más előnyök a Node.js folyamat hosszú ideig tart.
Remélem, ez a bejegyzés segít abban, hogy tapasztalt PHP-fejlesztőként többet hozzon ki a Node.js-ből.