Mise en cache en JavaScript : Redis, Memcached et solutions intégrées (niveau avancé)
\n\nDans le développement web moderne, les performances d'une application se heurtent souvent à la vitesse d'accès aux données. Chaque requête supplémentaire à la base de données ou à une API externe augmente le temps de réponse et la charge du serveur. La mise en cache n'est pas simplement "sauvegarder un résultat", c'est toute une science qui implique le choix de la bonne stratégie, du bon outil et du bon mécanisme d'invalidation. Dans cet article, nous allons explorer les techniques avancées de mise en cache dans Node.js : des solutions intégrées aux systèmes industriels Redis et Memcached.
\n\n1. Mise en cache intégrée dans Node.js : simplicité et limites
\n\nAvant d'intégrer des services externes, il convient d'évaluer les capacités de Node.js lui-même. La mise en cache intégrée est réalisée à l'aide d'objets Map ou WeakMap, ainsi que de simples variables globales. C'est idéal pour les données qui changent rarement et qui vivent au sein d'un seul processus.
// Cache mémoire simple avec TTL (Time To Live)\nclass MemoryCache {\n constructor(ttl = 60000) {\n this.cache = new Map();\n this.ttl = ttl;\n }\n\n set(key, value) {\n this.cache.set(key, {\n value,\n expiry: Date.now() + this.ttl\n });\n }\n\n get(key) {\n const item = this.cache.get(key);\n if (!item) return null;\n if (Date.now() > item.expiry) {\n this.cache.delete(key);\n return null;\n }\n return item.value;\n }\n\n delete(key) {\n this.cache.delete(key);\n }\n\n clear() {\n this.cache.clear();\n }\n}\n\n// Utilisation\nconst cache = new MemoryCache(30000); // TTL de 30 secondes\ncache.set('user:42', { name: 'Alice', role: 'admin' });\nconsole.log(cache.get('user:42')); // { name: 'Alice', role: 'admin' }\n\n\nAvantages : latence réseau nulle, simplicité de mise en œuvre, aucune dépendance externe requise.
\nInconvénients : les données sont perdues lors du redémarrage du processus, ne s'adapte pas à plusieurs serveurs, limité par la mémoire vive d'un seul nœud.
Pour les microservices fonctionnant dans un seul processus, ou pour la mise en cache des résultats de calculs lourds (par exemple, le rendu de templates), la solution intégrée est un excellent choix. Cependant, pour les systèmes distribués, elle est insuffisante.
\n\n2. Redis : le roi de la mise en cache dans Node.js
\n\nRedis est un magasin de données en mémoire souvent utilisé comme cache haute performance. Ses principaux avantages : prise en charge de structures de données complexes (chaînes, listes, ensembles, hachages), TTL intégré, pub/sub et persistance sur disque.
\n\nConnexion de Redis dans Node.js
\n\nPour travailler avec Redis dans Node.js, on utilise le package ioredis (recommandé) ou redis. Voici un exemple avec ioredis :
const Redis = require('ioredis');\n\nconst redis = new Redis({\n host: 'localhost',\n port: 6379,\n retryStrategy: (times) => Math.min(times * 50, 2000) // stratégie de reconnexion\n});\n\n// Opérations de base\nawait redis.set('mykey', 'Bonjour, Redis !', 'EX', 60); // TTL de 60 secondes\nconst value = await redis.get('mykey');\nconsole.log(value); // "Bonjour, Redis !"\n\n// Utilisation d'un hachage pour stocker des objets complexes\nawait redis.hmset('user:100', {\n name: 'Bob',\n email: 'bob@example.com',\n age: 30\n});\nconst user = await redis.hgetall('user:100');\nconsole.log(user); // { name: 'Bob', email: 'bob@example.com', age: '30' }\n\n\nStratégies avancées avec Redis
\n\nCache-Aside (Lazy Loading) : l'application vérifie d'abord le cache, et seulement en cas d'échec, elle interroge la base de données et remplit le cache.
\n\nasync function getUser(userId) {\n const cacheKey = `user:${userId}`;\n let user = await redis.get(cacheKey);\n if (user) {\n return JSON.parse(user);\n }\n // Échec du cache — chargement depuis la BD\n user = await db.findUserById(userId);\n if (user) {\n await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600); // cache pour 1 heure\n }\n ret