javascriptnode.jsexpresswebsocketws

How to broadcast a message to all clients with different worker in ws and express?


In my express app, I am using clusters and ws package to run project with a socket connection. When a client connect to socket one of workers handle the connection (For example worker with id 1). But when another client connected, then another worker handle the connection (worker with id 2). If client 1 send a message to broadcast, just clients with same worker id received that and client 2 received nothing. How can I broadcast data to all clients with different worker id. It works when I do not use clusters.

const run = () => {
  if (cluster.isPrimary) {
    for (let index = 0; index < numberCPUs; index++) {
      cluster.fork();
    }
    cluster.on("exit", (worker, code, signal) => {
      console.error(`worker ${worker.process.pid} died (${signal || code}). restarting it in a sec`);
      setTimeout(() => cluster.fork(), 1000);
    });
  } else {
    console.log(`Worker ${cluster.worker.id} : started`);
    const http = require("node:http");
    const WebSocket = require("ws");

    const server = http.createServer(application);
    const wss = new WebSocket.Server({ server });

    wss.on("connection", (ws, request, client) => {
      console.log(`Worker ${cluster.worker.id} handled new connection !`);
      ws.on("message", (message) => {
        console.log(`Worker ${cluster.worker.id} : Received message ${message}`);
        broadcast(WebSocket, wss, message);
      });
    });

    server.listen(port, () => {
      console.log(`Server is running on ${port} ...`);
    });
  }
};

Broadcast function :

const broadcast = (webSocket, wss, message) => {
  for (const client of wss.clients) {
    if (client.readyState === webSocket.OPEN) {
      client.send(message, { binary: false });
    }
  }
};

Solution

  • With the help of this answer I resolved the problem. I used publish and subscribe in redis. I created a subscribe channel in my app.js file :

    const server = http.createServer(application);
    const wss = new WebSocket.Server({ server });
    const WebSocket = require("ws");
    const redis = require("redis");
    
    
    // other codes ...
    
    const redisClient = redis.createClient({
      legacyMode: true,
    });
    const broadcastClient = redis.createClient();
    (async () => {
      await broadcastClient.connect();
      broadcastClient.subscribe("broadcast-channel", (message) => {
        for (const client of wss.clients) {
          if (client.readyState === WebSocket.OPEN) {
            client.send(message);
          }
        }
      });
    
      wss.on("connection", (ws) => {
        ws.on("message", (message) => {
          console.log(`Message: ${message}`);
        });
      });
    })();
    
    const run = () => {
      if (cluster.isPrimary) {
        for (let index = 0; index < numberCPUs; index++) {
          cluster.fork();
        }
        cluster.on("exit", (worker, code, signal) => {
          console.error(`worker ${worker.process.pid} died (${signal || code}). restarting it in a sec`);
          setTimeout(() => cluster.fork(), 1000);
        });
      } else {
        server.listen(port, () => {
          console.log(`Worker ${cluster.worker.id} : started on ${port}`);
        });
      }
    };
    

    Then with publish, I sent my messages. This is my other file that I want to broadcast my message in:

    const redis = require("redis");
    
    const redisClient = redis.createClient();
    await redisClient.connect();
    redisClient.publish("broadcast-channel", "Hellow world!");