WebSocket en JavaScript : applications temps réel avancées sur Node.js

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Utilisation avancée de WebSocket en JavaScript : du prototype à la production

\n

WebSocket est un protocole qui assure une communication bidirectionnelle entre un client et un serveur via une seule connexion TCP persistante. Dans des articles précédents, nous avons abordé les bases : établissement de la connexion, envoi de messages et chats simples. Cependant, pour créer des applications temps réel véritablement fiables et évolutives (jeux en ligne, plateformes de trading, systèmes d'édition collaborative), il est nécessaire de maîtriser des techniques avancées. Dans ce document, nous examinerons l'architecture d'un serveur WebSocket de qualité production sur Node.js, incluant la mise à l'échelle, la gestion d'état, l'autorisation et la gestion des erreurs.

\n\n

1. Architecture avancée du serveur et gestion des salles

\n

Un serveur WebSocket très simple (basé sur la bibliothèque ws) traite tous les clients dans un seul objet global. Dans les projets réels, une isolation logique est nécessaire — les salles (rooms) et les canaux.

\n

Considérons un exemple d'architecture utilisant la classe RoomManager :

\n
const WebSocket = require('ws');\n\nclass RoomManager {\n    constructor() {\n        this.rooms = new Map(); // roomName -> Set<WebSocket>\n    }\n\n    join(ws, roomName) {\n        if (!this.rooms.has(roomName)) {\n            this.rooms.set(roomName, new Set());\n        }\n        this.rooms.get(roomName).add(ws);\n        ws.room = roomName; // Stocke la salle sur le socket\n    }\n\n    leave(ws) {\n        if (ws.room && this.rooms.has(ws.room)) {\n            this.rooms.get(ws.room).delete(ws);\n            if (this.rooms.get(ws.room).size === 0) {\n                this.rooms.delete(ws.room);\n            }\n        }\n    }\n\n    broadcast(roomName, message, excludeWs = null) {\n        if (!this.rooms.has(roomName)) return;\n        this.rooms.get(roomName).forEach(client => {\n            if (client !== excludeWs && client.readyState === WebSocket.OPEN) {\n                client.send(JSON.stringify(message));\n            }\n        });\n    }\n}\n\n// Utilisation\nconst wss = new WebSocket.Server({ port: 8080 });\nconst roomManager = new RoomManager();\n\nwss.on('connection', (ws) => {\n    ws.on('message', (data) => {\n        const msg = JSON.parse(data);\n        switch (msg.type) {\n            case 'join':\n                roomManager.join(ws, msg.room);\n                break;\n            case 'message':\n                roomManager.broadcast(ws.room, { user: msg.user, text: msg.text }, ws);\n                break;\n        }\n    });\n\n    ws.on('close', () => roomManager.leave(ws));\n});
\n

Ce code permet de créer des chats isolés ou des sessions de jeu. Notez la gestion de la fermeture de connexion — elle est cruciale pour éviter les fuites mémoire.

\n\n

2. Mise à l'échelle : clustering et Redis Pub/Sub

\n

Un seul processus Node.js ne peut pas utiliser efficacement plus d'un cœur CPU. Pour la mise à l'échelle, nous utilisons le module intégré cluster. Cependant, un problème se pose : les connexions WebSocket sont liées à un worker spécifique. Comment envoyer un message à un utilisateur connecté à un autre worker ?

\n

La solution — un courtier de messages externe, par exemple Redis Pub/Sub.

\n
const cluster = require('cluster');\nconst http = require('http');\nconst { Server } = require('ws');\nconst redis = require('redis');\n\nconst REDIS_CHANNEL = 'ws:messages';\n\nif (cluster.isMaster) {\n    // Lance les workers selon le nombre de cœurs\n    const numCPUs = require('os').cpus().length;\n    for (let i = 0; i < numCPUs; i++) {\n        cluster.fork();\n    }\n\n    cluster.on('exit', (worker) => {\n        console.log(`Worker ${worker.process.pid} died`);\n        cluster.fork(); // Redémarrage\n    });\n} else {\n    // Worker\n    const server = http.createServer();\n    const wss = new Server({ server });\n\n    // Connexion à Redis\n    const pub = redis.createClient();\n    const sub                

Blogs

Book Recommendations