Terwijl de populariteit van Node.js toeneemt, neemt de tractie van PHP af. Met die context, gaat deze post dieper in op 5 must-know praktische aspecten van het gebruik van Node.js voor PHP ontwikkelaars. Dit zullen dingen zijn waar niemand over praat of schrijft, tijd om aan de slag te gaan.
- Node.js voor PHP ontwikkelaars (niet Node.js vs PHP)
- Node.js voor PHP ontwikkelaars de praktische kant
- Node.js code executie is async en niet sequentieel
- Node.js belooft mogelijkheden
- Node.js proces is long-running, in tegenstelling tot PHP
- Memoization voorbeeld
- Connection Pool voorbeeld met MySQL
- Debugging is eenvoudiger in Node.js dan in PHP
- Grote versie-upgrades in Node.js zijn naadloos ten opzichte van PHP
- Dockerizing van een Node.js applicatie is een peulenschil vergeleken met PHP
- Node.js code executie is async en niet sequentieel
- Conclusie
Node.js voor PHP ontwikkelaars (niet Node.js vs PHP) #
Dit stuk is een lijst van dingen die je als PHP ontwikkelaar moet weten en leren om Node.js effectief te gebruiken. Integendeel, dit stuk is geen Node.js vs PHP stuk waarin PHP wordt gebasht. Ik heb beide talen gebruikt. Ik begon meer Node.js te schrijven in 2016. Toen ik begon had ik wat moeilijkheden omdat ik meer dan 7 jaar daarvoor op mijn werk PHP gewend was. Er was een boek uitgebracht tegen het einde van 2012 dat Node.js behandelde voor PHP-ontwikkelaars.
Deze blogpost gaat het niet hebben over wat PHP of Node.js is, je kunt er in andere posts over lezen. Ik zal ook niet veel praten over de Non-Blocking I/O of de event loop. Toch zal een deel ervan worden doorgenomen bij het bespreken van de praktische aspecten van het schrijven van goede Node.js code.
Node.js voor PHP ontwikkelaars de praktische kant #
PHP bestaat al sinds 1995 en wordt naar verluidt nog steeds gebruikt door 79,% van de websites die door W3tech worden gecontroleerd (ik kan niet echt zeggen of het het hele internet is). De kans is dus groot dat je PHP hebt gebruikt of iets hebt gebruikt dat in PHP is geschreven. Bijvoorbeeld met een groeiende trend:
WordPress wordt gebruikt door 63,7% van alle websites waarvan we het contentmanagementsysteem kennen. Dit is 39,0% van alle websites die door W3Tech worden gemonitord.
Aan de andere kant werd Node.js in 2009 uitgebracht. Grote technische bedrijven zoals Linked In en Paypal begonnen het in 2011-2013 te gebruiken om verschillende redenen, zoals microservices. Volgens het Stack Overflow-ontwikkelaarsonderzoek van 2020:
Voor het tweede jaar op rij staat Node.js bovenaan, aangezien het door de helft van de respondenten wordt gebruikt.
Het is geen geheim dat Node.js de afgelopen 5 jaar erg populair is geworden.
Dus als PHP-ontwikkelaar zijn dit 5 praktische dingen die je moet weten om een goede Node.js-softwareontwikkelaar te worden. Node.js voor PHP-ontwikkelaars is vergelijkbaar in sommige opzichten, maar ook anders in een aantal andere aspecten die hieronder worden beschreven:
Node.js code executie is async en niet-sequentieel #
Dit is een gedrag dat veel PHP-ontwikkelaars misleidt. In PHP loopt de code in volgorde, eerst regel 1 dan 2, enzovoort. In Javascript en vooral in Node.js is dat misschien niet het geval. Je kunt mogelijk dingen op de achtergrond zetten met goed gebruik van promises en callbacks.
Hieronder staat een aangepast code voorbeeld met uitleg, genomen uit mijn 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 };
}
Als je beter kijkt, die onschuldig ogende db.query
op regel 3, is op de achtergrond gezet. Dus het zal als volgt worden uitgevoerd:
- Get rate
- Run insert query in de achtergrond
- Terwijl de insert wordt uitgevoerd is de functie al teruggegeven van de rate
- Als er een probleem is in de insert query wordt het gelogd in de catch
Er is geen out of the box manier om zoiets te doen in PHP. Dit is het eerste wat PHP ontwikkelaars tegen de borst stuit. Het maakt het moeilijker om Node.js te begrijpen voor PHP ontwikkelaars. Dit asynchrone gedrag bij het uitvoeren van code maakt het ook moeilijker om de juiste stack trace te vinden in geval van fouten in Node.js.
Om eerlijk te zijn kun je in 2020 gemakkelijk async await gebruiken. Ook al is het syntactische suiker op Promises, het maakt asynchroon programmeren wel een stuk eenvoudiger. Toen ik begon in het Node 4/6 tijdperk rond 2016 met callbacks en Promises was het een heel ander balspel. Toch, pas op wanneer je geen async-await gebruikt (zoals hierboven) en gewoon met promises, then en catch gaat werken. Raak daarbij echter niet verstrikt in de belofte hel. De beloftehel is de volgende iteratie van de callback-hel.
Pro tip: om te weten welke ES6-functies je met welke versie van Node.js kunt gebruiken, kijk je op node.green.
Andere pro tip:
Even Node.js-versies zijn LTS, oneven versies niet. Gebruik dus Node 14 of 16 en geen 13 of 15 in productie.
Wanneer we wat dieper ingaan op niet-sequentiële uitvoering, spelen promises en de kracht ervan hier een belangrijke rol. De mogelijkheid om gelijktijdige dingen te doen is geweldig in Node.js en javascript in het algemeen.
Node.js promises mogelijkheden #
Promises zijn asynchroon, je kunt ze gelijktijdig uitvoeren. Er zijn manieren om het te doen. Je zou 3 beloftes kunnen racen en de resultaten van de snelste krijgen. Je kunt zelfs promise.all
doen, als één belofte wordt afgewezen, stopt de hele operatie. Lees meer over Promise.race
, promise.all
en promise.any
in deze geweldige vergelijking.
Met dat in gedachten, kun je andere NPM-bibliotheken proberen om belofteconcurrency te beperken of zelfs gelijktijdig door beloften te filteren. Je kunt er wat van met ReactPHP. Maar het is niet opgenomen in de native PHP, zelfs niet in PHP 8. Dit is iets nieuws om je hoofd rond te wikkelen in Node.js voor PHP-ontwikkelaars.
Laten we doorgaan naar het volgende punt, het proces hoeft niet te sterven in Node.js zoals in PHP.
Node.js proces is long-running, in tegenstelling tot PHP #
PHP is bedoeld om te sterven niet in de zin dat het niet zal worden gebruikt. In de zin dat alle PHP processen moeten sterven. PHP is niet echt ontworpen voor langlopende taken/processen. In PHP als er een nieuw HTTP verzoek binnenkomt start de verwerking, na het terugsturen van het antwoord wordt het proces gedood. Dat is hoe PHP werkt. Dat creëert de behoefte aan FPM en andere servers. Je kunt stellen dat PHP 20+ jaar geleden al serverloos ontworpen was. Ik laat dat aan jou over.
Aan de andere kant is Node.js een lang lopend proces. Dit stelt u in staat om informatie te delen tussen verzoeken als dezelfde server / proces is de behandeling van meerdere verzoeken. Met een lang lopend proces, kun je eenvoudig gebruik maken van dingen als memoization op het geheugen en connection pooling voor een database. Het opent andere mogelijkheden zoals het tellen van het aantal gelijktijdige verzoeken op dat proces bijvoorbeeld.
Memoization voorbeeld #
Als je Memoization niet kent.
Memoization is een hogere-orde functie die een andere functie cached. Het kan van sommige langzame functies snelle functies maken. Het resultaat van een functie-aanroep wordt na de eerste keer opgeslagen in de cache, dus als u de functie opnieuw aanroept met dezelfde argumenten, wordt deze gevonden in de cache.
Het kan worden gebruikt in Node.js, maar niet in PHP zelf. Er is wel een workaround mogelijk in PHP, zoals het opslaan van de return value van de functie in Redis.
Hieronder staat een codevoorbeeld van memoization op een express route met 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);
}
});
Het duidelijke voordeel hiervan is minder belasting van de datastore. Gedurende 1 minuut, zal het terug antwoorden met hetzelfde antwoord voor dezelfde parameters. De uitvoer van de functie products.getMultiple
wordt een minuut lang in het geheugen gecached. Dit maakt de antwoorden zeer snel.
Connection Pool voorbeeld met MySQL #
Een ander ding dat niet mogelijk is als gevolg van een stervend proces in PHP is connection pooling. Volgens Wikipedia:
In software engineering is een verbindingspool een cache van databaseverbindingen die wordt bijgehouden zodat de verbindingen kunnen worden hergebruikt wanneer toekomstige verzoeken aan de database nodig zijn. Verbindingspools worden gebruikt om de prestaties van het uitvoeren van opdrachten op een database te verbeteren.
Dus, je hebt 5 verbindingen in een pool en als je 5 query’s op de database wilt uitvoeren, kan dat gelijktijdig worden gedaan. Dit bespaart tijd voor zowel het verbinden met de database als het uitvoeren van de query. Dit is eenvoudig te doen in Node.js, maar niet eenvoudig mogelijk in PHP.
Houd rekening met het aantal beschikbare verbindingen en zorg ervoor dat de grootte van uw verbindingspool optimaal is.
Bijv. als u Kubernetes gebruikt en uw toepassing 5 pods heeft met een verbindingspoolgrootte van 2. Dat betekent dat uw database altijd 10 verbindingen heeft. Dat betekent dat uw database altijd 10 open verbindingen zal hebben, ook al worden er geen query’s uitgevoerd.
Tijd voor een connectiepool-voorbeeld met MySQL-database met MySQL npm-module:
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
});
}
De bovenstaande code zal dezelfde query 5 keer parallel uitvoeren met 5 MySQL-verbindingen die uit de connectiepool worden gehaald. Ik wou dat ik dit kon doen in PHP out of the box.
In mijn ervaring, Node.js werkt heel goed met Mysql. Als je verbindingspooling met Mongo DB wilt proberen, is hier een Mongo-voorbeeld.
Bij een lang lopend proces moet je als ontwikkelaar beter op geheugenlekken letten en de huishoudelijke taken goed uitvoeren.
Dit is waar Node.js voor PHP-ontwikkelaars een behoorlijke verschuiving nodig heeft in het denken over hoe de code wordt uitgevoerd. Aan de andere kant is dit een groot voordeel in Node.js voor PHP-ontwikkelaars.
Debugging is eenvoudiger in Node.js dan in PHP #
Line by line code debugging is een belangrijk onderdeel van ontwikkelaarservaring voor elke programmeertaal. Om PHP-code te debuggen, kun je add-ons als X-Debug gebruiken met bepaalde IDE-instellingen. X-Debug is op zijn zachtst gezegd een uitdaging om op te zetten. Je moet het installeren, de extensie inschakelen. Daarna moet je het goed configureren met een IDE zoals PHPStorm.
Makkelijk is wel het laatste wat je zult zeggen over het laten werken van X-debug. Tenzij het allemaal goed is geconfigureerd met een dockercontainer en de IDE-instellingen ook gemakkelijk te laden zijn.
Aan de andere kant is het uitvoeren van node native debugger of zelfs ndb een stuk eenvoudiger in vergelijking met PHP en X-debug. Met behulp van VS Code is het debuggen van Node.js-toepassingen zo eenvoudig dat zelfs een holbewoner het kan doen.
Open Voorkeuren > Instellingen en typ in het zoekvak “node debug”. Onder het tabblad Extensies moet er een extensie zijn met de naam “Node debug”. Van hieruit, klik op het eerste vakje: Debug > Node: Auto Attach en zet de drop-down op “on”. Je bent nu bijna klaar om te gaan. Ja, het is echt zo gemakkelijk.
Stel vervolgens een aantal breekpunten in op de VS-code met bijvoorbeeld index.js
en typ in de terminal node --inspect index.js
.
BOEM! Je stap voor stap Node.js debugger draait goed op de VS Code editor zonder veel moeite. Een goed verschil met PHP, het is niet nodig om een andere extensie te installeren, deze in te schakelen en te configureren om een programma te kunnen debuggen. Geen noodzaak om een extra extensie te installeren is een voordeel gevonden in Node.js voor PHP-ontwikkelaars.
Het volgende punt gaat ook over betere ontwikkelaar ervaring tijdens het upgraden, zelfs meerdere grote versies van de taal.
Major versie-upgrades in Node.js is naadloos over PHP #
Jumping zelfs meerdere grote versies in Node.js is een naadloze ervaring. Upgraden van PHP 5.x naar PHP 7.x is een proces van een week tot een maand, afhankelijk van de grootte en complexiteit van het project.
In mijn persoonlijke ervaring heb ik Node.js microservices in het verleden geüpgraded van versies 0.12 tot 4. Onlangs heb ik een applicatie geüpgraded van Node.js 10 naar 14. Al mijn upgrades van Node.js-versies zijn eenvoudig geweest.
Een paar kleine wijzigingen in package.json waren de enige kleine problemen die ik tegenkwam. Na de implementatie waren er zelden problemen met de compatibiliteit van de code. Als extra bonus waren de prestaties meestal beter bij het upgraden van de grote versies.
Het upgraden van PHP is daarentegen niet eenvoudig geweest. Een kleine versie-upgrade voor een toepassing van PHP 5.4 naar 5.6 was niet erg omslachtig. Maar, van PHP 5.6 naar 7.2 voor een relatief grote toepassing was een pijn. Het duurde lang, en er waren meerdere composer.json wijzigingen nodig. Het was ook een moeilijke taak om het te testen. De goede kant van een grote versie-upgrade in PHP was zeker de prestatieverbetering.
Let op: de PHP-toepassingen waar ik mee werkte waren ouder dan de Node.js-toepassingen. Uw ervaring kan zeker anders zijn dan de mijne.
Dockerizing van een Node.js-toepassing is een koud kunstje vergeleken met PHP.
De populariteit van Docker is de afgelopen vijf jaar gestaag toegenomen. Het heeft veranderd hoe wij software engineers werken sinds de release. Je zou Docker ook moeten gebruiken voor lokale ontwikkeling. Met dat in gedachten kan het Dockeriseren van een PHP applicatie een moeilijke taak zijn, afhankelijk van hoe de componenten zijn ingedeeld en de complexiteit van de applicatie. Omgekeerd, voor het dockeriseren van een Node.js applicatie is de inspanning minder en het proces is een eitje.
Hieronder staat een voorbeeld van een dockerfile voor een PHP Laravel app met 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
Het goede met deze Docker image voor Laravel is dat PHP gebundeld is met apache in dezelfde image. Er kan worden betoogd of dit een betere manier is om het te doen dan PHP en Apache in twee docker images te splitsen.
Ook de multi-stage docker build in de bovenstaande docker image is opgevallen. De installatie van Composer wordt gedaan in een ander image en de uitvoer wordt gekopieerd naar het hoofd image. Als we PHP-FPM en Nginx in verschillende docker images hadden gebruikt, zou het complexer zijn geweest. Er zouden twee verschillende docker-images moeten worden beheerd.
Nu is het tijd om eens naar een Node.js Dockerfile te kijken.
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
Omdat Node.js een ingebouwde webserver heeft, is de Dockerfile veel schoner.
Wanneer je node installeert, wordt npm meegebundeld. Hierdoor is het niet meer nodig om pakketten in een andere fase van de docker build te installeren.
In het bovenstaande Dockerfile wordt multi-stage docker build gebruikt om productie- en ontwikkelingsdocker-images van elkaar te scheiden. Het gebundeld hebben van de package manager (npm) en het hebben van de web-server als onderdeel van de taal/runtime is iets anders in Node.js voor PHP ontwikkelaars. Als je meer geïnteresseerd bent in het stap-voor-stap Docken van een Node.js applicatie, volg dan deze tutorial.
Conclusie #
Bij het gebruik van Node.js voor PHP ontwikkelaars is een kleine verandering in denken nodig om de krachten van Node.js goed te benutten. Node.js is geen wondermiddel. Er zijn nadelen en het moet worden aangepast aan verschillende manieren om code uit te voeren.
Zeker, er zijn enkele voordelen van het gebruik van Node.js voor PHP-ontwikkelaars, zoals async-programmering en concurrency. Andere voordelen vloeien voort uit de Node.js-proces lang duurt.
Ik hoop dat dit bericht je als ervaren PHP-ontwikkelaar helpt om meer uit Node.js te halen.