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

  1. Monitorització del Rendiment

    • Identificar colls d'ampolla
    • Eines de monitorització
  2. Gestió de la Memòria

    • Recollida de brossa (Garbage Collection)
    • Fuites de memòria
  3. Optimització del Codi

    • Millors pràctiques de codificació
    • Evitar operacions costoses
  4. Utilització de Caches

    • Cache de memòria
    • Cache de disc
  5. Escalabilitat

    • Mòdul Cluster
    • Load Balancing
  6. 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

db.collection.createIndex({ "field": 1 });

Exercicis Pràctics

  1. Monitorització del Rendiment: Utilitza Performance Hooks per mesurar el temps d'execució d'una funció que llegeix un fitxer gran.
  2. Gestió de la Memòria: Implementa un codi que generi un "heap dump" cada 30 segons i analitza els resultats.
  3. Optimització del Codi: Refactoritza un codi que utilitza operacions síncrones per utilitzar operacions asíncrones.
  4. Utilització de Caches: Implementa una cache de memòria utilitzant redis per emmagatzemar els resultats d'una consulta a la base de dades.
  5. 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

Mòdul 7: Bases de Dades i ORMs

Mòdul 8: Autenticació i Autorització

Mòdul 9: Proves i Depuració

Mòdul 10: Temes Avançats

Mòdul 11: Desplegament i DevOps

Mòdul 12: Projectes del Món Real

© Copyright 2024. Tots els drets reservats