En aquest tema, aprendrem com optimitzar el rendiment de les aplicacions Node.js. La optimització del rendiment és crucial per assegurar que les aplicacions siguin ràpides, eficients i escalables. A continuació, desglossarem els conceptes clau, proporcionarem exemples pràctics i oferirem exercicis per reforçar els coneixements.
Conceptes Clau
-
Monitorització del Rendiment
- Identificar colls d'ampolla
- Eines de monitorització
-
Gestió de la Memòria
- Recollida de brossa (Garbage Collection)
- Fuites de memòria
-
Optimització del Codi
- Millors pràctiques de codificació
- Evitar operacions costoses
-
Utilització de Caches
- Cache de memòria
- Cache de disc
-
Escalabilitat
- Mòdul Cluster
- Load Balancing
-
Optimització de la Base de Dades
- Índexs
- Consultes optimitzades
Monitorització del Rendiment
Identificar Colls d'Ampolla
Per optimitzar el rendiment, primer hem d'identificar els colls d'ampolla. Això es pot fer mitjançant eines de monitorització com:
- Node.js Performance Hooks: API integrada per mesurar el rendiment.
- New Relic: Plataforma de monitorització de rendiment.
- PM2: Gestor de processos amb capacitats de monitorització.
Exemple Pràctic: Utilitzar Performance Hooks
const { performance, PerformanceObserver } = require('perf_hooks'); const obs = new PerformanceObserver((items) => { console.log(items.getEntries()); performance.clearMarks(); }); obs.observe({ entryTypes: ['measure'] }); performance.mark('A'); setTimeout(() => { performance.mark('B'); performance.measure('A to B', 'A', 'B'); }, 1000);
Aquest codi mesura el temps que triga a executar-se un setTimeout
de 1000 ms.
Gestió de la Memòria
Recollida de Brossa (Garbage Collection)
Node.js utilitza el motor V8 de Google, que inclou un recollidor de brossa automàtic. Tot i això, és important escriure codi que minimitzi la creació d'objectes innecessaris.
Fuites de Memòria
Les fuites de memòria poden degradar el rendiment de l'aplicació. Utilitza eines com heapdump
i node-inspect
per identificar i solucionar fuites de memòria.
Exemple Pràctic: Detectar Fuites de Memòria
const heapdump = require('heapdump'); setInterval(() => { heapdump.writeSnapshot((err, filename) => { console.log('Heap dump written to', filename); }); }, 60000);
Aquest codi crea un "heap dump" cada minut per ajudar a identificar fuites de memòria.
Optimització del Codi
Millors Pràctiques de Codificació
- Evita operacions síncrones.
- Utilitza
async
/await
per a la programació asíncrona. - Minimitza l'ús de bucles intensius.
Exemple Pràctic: Evitar Operacions Costoses
// Evita això const data = fs.readFileSync('/file/path'); // Fes això fs.readFile('/file/path', (err, data) => { if (err) throw err; console.log(data); });
Utilització de Caches
Cache de Memòria
Utilitza eines com redis
o memcached
per emmagatzemar dades freqüentment accedides a la memòria.
Exemple Pràctic: Utilitzar Redis com a Cache
const redis = require('redis'); const client = redis.createClient(); client.on('error', (err) => { console.log('Error ' + err); }); client.set('key', 'value', redis.print); client.get('key', (err, reply) => { console.log(reply); // 'value' });
Escalabilitat
Mòdul Cluster
El mòdul cluster
permet crear múltiples processos de treball que comparteixen el mateix port del servidor.
Exemple Pràctic: Utilitzar el Mòdul Cluster
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); }); } else { http.createServer((req, res) => { res.writeHead(200); res.end('hello world\n'); }).listen(8000); }
Optimització de la Base de Dades
Índexs
Els índexs poden millorar significativament el rendiment de les consultes a la base de dades.
Consultes Optimitzades
Evita consultes no optimitzades i assegura't que les consultes utilitzen els índexs correctament.
Exemple Pràctic: Crear un Índex en MongoDB
Exercicis Pràctics
- Monitorització del Rendiment: Utilitza
Performance Hooks
per mesurar el temps d'execució d'una funció que llegeix un fitxer gran. - Gestió de la Memòria: Implementa un codi que generi un "heap dump" cada 30 segons i analitza els resultats.
- Optimització del Codi: Refactoritza un codi que utilitza operacions síncrones per utilitzar operacions asíncrones.
- Utilització de Caches: Implementa una cache de memòria utilitzant
redis
per emmagatzemar els resultats d'una consulta a la base de dades. - Escalabilitat: Utilitza el mòdul
cluster
per crear un servidor HTTP que utilitzi tots els nuclis de la CPU.
Solucions
Exercici 1: Monitorització del Rendiment
const { performance, PerformanceObserver } = require('perf_hooks'); const fs = require('fs'); const obs = new PerformanceObserver((items) => { console.log(items.getEntries()); performance.clearMarks(); }); obs.observe({ entryTypes: ['measure'] }); performance.mark('A'); fs.readFile('/path/to/large/file', (err, data) => { if (err) throw err; performance.mark('B'); performance.measure('A to B', 'A', 'B'); });
Exercici 2: Gestió de la Memòria
const heapdump = require('heapdump'); setInterval(() => { heapdump.writeSnapshot((err, filename) => { console.log('Heap dump written to', filename); }); }, 30000);
Exercici 3: Optimització del Codi
// Original const data = fs.readFileSync('/file/path'); // Refactoritzat fs.readFile('/file/path', (err, data) => { if (err) throw err; console.log(data); });
Exercici 4: Utilització de Caches
const redis = require('redis'); const client = redis.createClient(); client.on('error', (err) => { console.log('Error ' + err); }); const getData = (key, callback) => { client.get(key, (err, reply) => { if (reply) { callback(null, reply); } else { // Simula una consulta a la base de dades const data = 'result from database'; client.set(key, data, redis.print); callback(null, data); } }); }; getData('myKey', (err, data) => { console.log(data); });
Exercici 5: Escalabilitat
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); }); } else { http.createServer((req, res) => { res.writeHead(200); res.end('hello world\n'); }).listen(8000); }
Conclusió
En aquesta secció, hem après diverses tècniques per optimitzar el rendiment de les aplicacions Node.js, incloent la monitorització del rendiment, la gestió de la memòria, l'optimització del codi, l'ús de caches i l'escalabilitat. Aquests conceptes són fonamentals per assegurar que les aplicacions siguin ràpides, eficients i escalables. Amb els exercicis pràctics, has tingut l'oportunitat de posar en pràctica aquests conceptes i millorar les teves habilitats en optimització del rendiment.
Curs de Node.js
Mòdul 1: Introducció a Node.js
Mòdul 2: Conceptes Bàsics
Mòdul 3: Sistema de Fitxers i I/O
Mòdul 4: HTTP i Servidors Web
Mòdul 5: NPM i Gestió de Paquets
Mòdul 6: Framework Express.js
- Introducció a Express.js
- Configuració d'una Aplicació Express
- Middleware
- Routing en Express
- Gestió d'Errors
Mòdul 7: Bases de Dades i ORMs
- Introducció a les Bases de Dades
- Utilitzar MongoDB amb Mongoose
- Utilitzar Bases de Dades SQL amb Sequelize
- Operacions CRUD
Mòdul 8: Autenticació i Autorització
Mòdul 9: Proves i Depuració
- Introducció a les Proves
- Proves Unitàries amb Mocha i Chai
- Proves d'Integració
- Depuració d'Aplicacions Node.js
Mòdul 10: Temes Avançats
Mòdul 11: Desplegament i DevOps
- Variables d'Entorn
- Utilitzar PM2 per a la Gestió de Processos
- Desplegar a Heroku
- Integració i Desplegament Continu