skip to Main Content

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 });
    }
  }
};

2

Answers


  1. Chosen as BEST ANSWER

    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();
    broadcastClient
      .connect()
      .then(() => {
        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}`);
          });
        });
      })
      .catch((error) => {
        console.log(error);
      });
    
    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!");
    
    

  2. When using clusters in Node.js to handle WebSocket connections, you’re essentially running multiple instances of your application, each with its own set of worker processes. In your current approach, you’re trying to broadcast messages to clients using the broadcast function, which iterates over all clients connected to the WebSocket server (wss). This approach will only work for clients that are connected to the same worker process that the broadcasting client is connected to.
    To broadcast messages to all clients across different worker processes, you need a way to share the WebSocket connections or messages between different workers. One approach could be to use a centralised messaging mechanism like Redis Pub/Sub. This will allow cross-worker communication for broadcasting WebSocket messages.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search