Această poveste a fost publicată inițial pe blogul DeadSimpleChat:
Creați aplicația de chat Node.JS cu WebSocket, Node.js Cluster și Redis

„Dead Simple Chat” oferă chat predefinit cu „JavaScript Chat API și SDK”, care poate fi adăugat în câteva secunde la orice aplicație sau site web.

În această postare pe blog, vom construi o aplicație de chat extrem de scalabilă folosind WebSocket Node.JS și Redis.

Această aplicație de chat va fi de înaltă performanță și poate gestiona mulți utilizatori concurenți. Să începem.

Ce este Node.JS Cluster?

Node.JS este un timp de rulare javascript de înaltă performanță, dar este în primul rând cu un singur thread.

Asta înseamnă că, dacă aveți un computer cu mai multe nuclee CPU, pe care toate computerele moderne au aplicația dvs. Node.JS, nu va putea folosi toate nucleele CPU disponibile.

Node.JS va lansa implicit un singur proces care va rula pe un singur nucleu CPU. Dacă aveți un procesor cu 10 nuclee, atunci celelalte 9 nuclee vor fi utilizate de programul dvs. Node.JS.

Modulul cluster Node.JS este un modul Node.JS încorporat și își propune să rezolve exact această problemă. Modulul cluster vă permite să lansați mai multe procese secundare sau fire de lucru care s-ar executa pe alte nuclee CPU pentru a utiliza pe deplin nucleele multiple oferite în procesoarele moderne.

Vom folosi modulul Node.JS Cluster pentru a lansa mai multe fire de execuție pentru a gestiona conexiunile concurente la serverul nostru de chat pentru a extrage performanță maximă.

Ce sunt WebSockets?

Websocket-urile ne permit să avem o conexiune în timp real între clientul care este browser și serverul nostru de chat backend.

Acesta este clientul pentru a primi mesajele în timp real fără a interoga serverul și îi permite serverului să împingă mesajele către client.

Acesta creează o conductă bidirecțională între client și server care ne va permite să trimitem și să primim mesajele.

Pentru a gestiona WebSockets, vom folosi biblioteca „Socket.IO” care oferă un înveliș frumos în jurul WebSocket-urilor vanilie și oferă funcționalități suplimentare, cum ar fi conexiuni stick, care vor fi foarte necesare în aplicația noastră în cluster.

De ce să folosiți Redis pentru a stoca mesaje?

Redis este un magazin de date în memorie de înaltă performanță și este foarte rapid. Poate gestiona mii de operațiuni pe secundă pe un hardware modest.

Astfel, folosirea Redis pentru a stoca mesaje ar adăuga un decalaj minim la serverul nostru de mesaje de chat.

✴️Puteți folosi „Dead Simple Chat” ca soluție de chat în aplicație, care vă permite să adăugați chat de chat extrem de scalabil în câteva minute.

Pasul 1: Scafodarea aplicației

Să scheletăm aplicația, vom crea un director numit chat-application

mkdir chat-application

Apoi vom cd în director.

cd chat-application

Și vom rula comanda npm init

npm init -y

Comanda npm init va crea un fișier package.json.

De asemenea, creați un folder numit public care va conține front-end-ul nostru de chat.

mkdir public

Pasul 2: Instalarea dependențelor

Pentru aplicația noastră de chat, vom instala următoarele pachete:

  1. express — Express este un cadru minim de aplicații web care ne va permite să creăm cu ușurință o aplicație web în Node.JS
  2. ioredis — ioredis este un driver redis de înaltă performanță pentru Node.JS. Acest lucru ne va permite să ne conectăm la baza de date redis din aplicația noastră Node.JS.
  3. socket.io — socket.io este o bibliotecă WebSocket pe care o vom folosi, deoarece oferă suport încorporat de conexiune sticky pentru clustering.
  4. @socket.io/sticky și @socket.io/cluster-adapter — Aceste două pachete vor fi folosite pentru a configura conexiuni stick în firele de lucru.

Rulați următoarea comandă pentru a instala dependențele de mai sus

npm install express ioredis socket.io @socket.io/cluster-adapter @socket.io/sticky --save

Pasul 3: Configurarea grupării

Creați un fișier numit server.js, acesta va conține tot codul backend pentru serverul nostru de chat și deschideți fișierul server.js în editorul de coduri preferat.

Adăugați următorul cod în fișierul server.js:

const cluster = require('node:cluster');
const http = require('node:http');
const numCPUs = require('node:os').availableParallelism();
const process = require('node:process');
const { setupMaster, setupWorker } = require("@socket.io/sticky");
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
const { Server } = require("socket.io");
const { info } = require('node:console');
const express = require("express");

/**
 * Checking if the thread is a worker thread
 * or primary thread.
 */
if (cluster.isPrimary) {
    console.log(`Primary ${process.pid} is running`);

     /**
     * Creating http-server for the master.
     * All the child workers will share the same port (3000)
     */
    const httpServer = http.createServer();
    httpServer.listen(3000);

    // Setting up stick session
    setupMaster(httpServer, {
        loadBalancingMethod: "least-connection"
    });

    // Setting up communication between workers and primary
    setupPrimary();
    cluster.setupPrimary({
        serialization: "advanced"
    });

    // Launching workers based on the number of CPU threads.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`worker ${worker.process.pid} died`);
      });
} else {
    /**
     * Setting up the worker threads
     */

    console.log(`Worker ${process.pid} started`);

    /**
     * Creating Express App and Socket.io Server
     * and binding them to HTTP Server.
     */
    const app = express();
    const httpServer = http.createServer(app);
    const io = new Server(httpServer);

    // Using the cluster socket.io adapter.
    io.adapter(createAdapter());

    // Setting up worker connection with the primary thread.
    setupWorker(io);

    io.on("connection", (socket) => {
        // Handling socket connections.
        socket.on("message", (data) => {
            console.log(`Message arrived at ${process.pid}`);
        });
    });

    // Handle HTTP Requests
    app.get("/", (req, res) => {
        res.send("Hello world");
    });
}

În fragmentul de cod de mai sus, am creat un server socket.io și expres care rulează pe mai multe nuclee.

Verifică numărul de procesoare disponibile și lansează firele de lucru egale cu numărul de fire de procesare disponibile.

Pasul 4: Gestionarea mesajelor

De fiecare dată când un nou client se conectează la serverul de chat, blocul de cod io.on("connection", (socket) => { }); se execută.

io.on("connection", (socket) => {

});

Obiectul socket ne permite sa comunicam bi-directional cu clientul, putem asculta evenimentul emis de client si putem de asemenea sa emitem evenimente catre client.

io.on("connection", (socket) => {
        // Handling socket connections.
        socket.on("message", (data) => {
            console.log(`Message arrived at ${process.pid}:`, data);

            socket.emit("message", data);
        });
    });

În blocul de cod de mai sus, ascultăm evenimentul message, numele evenimentului poate fi orice.

Și apoi emitem un eveniment de mesaj către client cu niște date. Datele pot fi orice, pot fi un șir sau un obiect JSON.

Trimiterea de mesaje către toți clienții

Evenimentul socket.emit trimite doar mesajul către client, clientul cu care este asociat obiectul socket.

Dacă doriți să trimiteți mesaje tuturor clienților conectați, atunci puteți utiliza metoda io.broadcast.emit("event", message)

Metoda io.broadcast.emit va trimite mesajul tuturor clienților conectați.

Dacă doriți să trimiteți mesajul tuturor clienților, cu excepția instanței actuale de socket, atunci puteți utiliza metoda socket.broadcast.emit("event", message);

Creați un server de mesaje de chat de bază

Un server de mesaje de chat foarte simplu ar fi un server de mesaje de chat care trimite un mesaj tuturor clienților conectați atunci când primește un mesaj nou.

Codul pentru asta va arăta așa:

io.on("connection", (socket) => {
    // Handling socket connections.
    socket.on("message", (data) => {
        console.log(`Message arrived at ${process.pid}:`, data);

        io.broadcast.emit("message", data);
    });
});

Cu siguranță vom îmbunătăți acest lucru, adăugând validare la mesaje, dezinfectând mesajele și stocându-le în baza noastră de date Redis, dar în afară de asta, designul serverului de mesaje de chat este aproape complet.

Cu doar câteva linii de cod, am construit un server de chat extrem de scalabil. Acum să construim front-end-ul și apoi, după aceea, vom reveni la adăugarea de stocare persistentă pentru mesaje.

Aceasta este povestea de până acum. Pentru a citi povestea completă, accesați blogul DeadSimpleChat:
Creați aplicația de chat Node.JS cu WebSocket, Node.js Cluster și Redis