Git Product home page Git Product logo

socket.io-cluster-adapter's Introduction

Socket.IO cluster adapter

The @socket.io/cluster-adapter package allows broadcasting packets between multiple Socket.IO servers.

Adapter diagram

It can be used in conjunction with @socket.io/sticky to broadcast packets between the workers of the same Node.js cluster.

Supported features:

Related packages:

Table of contents

Installation

npm install @socket.io/cluster-adapter

Usage

const cluster = require("cluster");
const http = require("http");
const { Server } = require("socket.io");
const numCPUs = require("os").cpus().length;
const { setupMaster, setupWorker } = require("@socket.io/sticky");
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  const httpServer = http.createServer();

  // setup sticky sessions
  setupMaster(httpServer, {
    loadBalancingMethod: "least-connection",
  });

  // setup connections between the workers
  setupPrimary();

  // needed for packets containing buffers (you can ignore it if you only send plaintext objects)
  // Node.js < 16.0.0
  cluster.setupMaster({
    serialization: "advanced",
  });
  // Node.js > 16.0.0
  // cluster.setupPrimary({
  //   serialization: "advanced",
  // });

  httpServer.listen(3000);

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on("exit", (worker) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork();
  });
} else {
  console.log(`Worker ${process.pid} started`);

  const httpServer = http.createServer();
  const io = new Server(httpServer);

  // use the cluster adapter
  io.adapter(createAdapter());

  // setup connection with the primary process
  setupWorker(io);

  io.on("connection", (socket) => {
    /* ... */
  });
}

License

MIT

socket.io-cluster-adapter's People

Contributors

darrachequesne avatar rolandoandrade avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

socket.io-cluster-adapter's Issues

Cluster Adapter Type is not compatible with io adapter

Im using Express.js and socket.io/pm2 + typescript.

const clusterAdapter = createAdapter()(nsp);
io.adapter(clusterAdapter);
setupWorker(io);

io.adapter accepts AdapterConstructor
the error:

Argument of type 'ClusterAdapter' is not assignable to parameter of type 'AdapterConstructor'.
Type 'ClusterAdapter' is missing the following properties from type 'typeof Adapter': prototype, getEventListeners, addAbortListener, errorMonitor, and 5 more

error when npm install @socket.io/cluster-adapter

At line:1 char:13

  • npm install @socket.io/cluster-adapter
  •         ~~~~~~~
    

The splatting operator '@' cannot be used to reference variables in an
expression. '@socket' can be used only as an argument to a command. To
reference variables in an expression use '$socket'.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorReco
rdException
+ FullyQualifiedErrorId : SplattingNotPermitted
Screenshot 2023-10-19 165609

io.local.emit fails but io.local.fetchSockets works

I am using Socket.IO client and server 4.5.3 together with cluster adapter 0.2.1 and sticky 1.0.2.

When I do io.local.emit from a node (child worker) in the cluster, nothing happens. It seems like the emit doesn't work.

When I replace the emit by a loop over the result of io.local.fetchSockets, it does works. This seems like a bug. I cannot find anything online about people using io.local.

ERR_IPC_CHANNEL_CLOSED and EPIPE errors on exit event

Hey! Nice work! I found an issue when I try to run a process in cluster mode using this library.

Description

When I run a process in cluster mode using the @socket.io/pm2 library and I try to delete them using pm2 delete all I get the following errors:

PM2             | Error [ERR_IPC_CHANNEL_CLOSED]: Channel closed
PM2             |     at ChildProcess.target.send (internal/child_process.js:680:16)
PM2             |     at Worker.send (internal/cluster/worker.js:45:28)
PM2             |     at EventEmitter.<anonymous> (/usr/lib/node_modules/@socket.io/pm2/node_modules/@socket.io/cluster-adapter/dist/index.js:392:32)
PM2             |     at EventEmitter.emit (events.js:314:20)
PM2             |     at EventEmitter.emit (domain.js:483:12)
PM2             |     at ChildProcess.<anonymous> (internal/cluster/master.js:191:13)
PM2             |     at Object.onceWrapper (events.js:421:26)
PM2             |     at ChildProcess.emit (events.js:314:20)
PM2             |     at ChildProcess.EventEmitter.emit (domain.js:506:15)
PM2             |     at Process.ChildProcess._handle.onexit (internal/child_process.js:276:12) {
PM2             |   code: 'ERR_IPC_CHANNEL_CLOSED'
PM2             | }
PM2             | Error: write EPIPE
PM2             |     at ChildProcess.target._send (internal/child_process.js:807:20)
PM2             |     at ChildProcess.target.send (internal/child_process.js:678:19)
PM2             |     at Worker.send (internal/cluster/worker.js:45:28)
PM2             |     at EventEmitter.<anonymous> (/usr/lib/node_modules/@socket.io/pm2/node_modules/@socket.io/cluster-adapter/dist/index.js:392:32)
PM2             |     at EventEmitter.emit (events.js:314:20)
PM2             |     at EventEmitter.emit (domain.js:483:12)
PM2             |     at ChildProcess.<anonymous> (internal/cluster/master.js:191:13)
PM2             |     at Object.onceWrapper (events.js:421:26)
PM2             |     at ChildProcess.emit (events.js:314:20)
PM2             |     at ChildProcess.EventEmitter.emit (domain.js:506:15) {
PM2             |   errno: 'EPIPE',
PM2             |   code: 'EPIPE',
PM2             |   syscall: 'write'
PM2             | }

It seems on exit it is trying to send the WORKER_EXIT message to disconnected workers.

cluster.on("exit", (worker) => {
// notify all active workers
for (const workerId in cluster.workers) {
if (hasOwnProperty.call(cluster.workers, workerId)) {
cluster.workers[workerId].send({
source: MESSAGE_SOURCE,
type: EventType.WORKER_EXIT,
data: worker.id,
});
}
}
});

if we set up pm2 to resurrect on failure, this causes the unexpected resurrection of the killed/deleted/stopped processes.

Based on this thread I think it is related to a synchronization error when multiple processes are closed at once. I was also able to delete the processes one by one without any problem.

I could mitigate the error adding a callback to handle the exception:

    cluster.on("exit", (worker) => {
        // notify all active workers
        for (const workerId in cluster.workers) {
            if (hasOwnProperty.call(cluster.workers, workerId)) {
                cluster.workers[workerId].send({
                    source: MESSAGE_SOURCE,
                    type: EventType.WORKER_EXIT,
                    data: worker.id,
                }, (err) => {
                    if (err) {
                        if (err.code == 'ERR_IPC_CHANNEL_CLOSED' || err.code == 'EPIPE') {
                            console.warn(`There was a synchronization problem. Wrong attempt to send a message to a disconnected worker`)
                            console.log(err);
                        } else {
                            throw err;
                        }
                    }
                });
            }
        }
    });

Steps to reproduce

  1. Create a sample process.
process.on('SIGINT', () => {
    console.log(`Received SIGINT.  Shutting down.`);
    process.exit(0);
});

let i = 0;

async function run() {
    while (true) {
        await new Promise(resolve => setTimeout(resolve, 5000));
        console.log(`Number ${i++}.`);
    }
}

run().catch(error => {
    console.error('Error!', error);
    process.exit();
});
  1. Delete pm2 and install @socket.io/pm2
  2. Run the process in cluster mode
pm2 start test-process.js -i 3
  1. Try to delete/stop/kill the processes.
pm2 delete all

After doing this, you will see the exceptions.

Env details

  • node 16
  • socket.io-cluster-adapter 0.1.0
  • socket.io/pm2 latest

Dependency on [email protected] should be upgraded to [email protected]

Hi, I think something went wrong during a recent upgrade.

The function createAdapter() from @socket.io/[email protected] returns (nsp: any) => ClusterAdapter where ClusterAdapter extends Adapter from [email protected]. However, server.adapter from [email protected] expects Adapter from [email protected]. There are large differences between these two classes.

Type 'ClusterAdapter' is missing the following properties from type 'Adapter': _encode, persistSession, restoreSession ...... 

Error trying to call function .fetchSockets(): timeout reached: only 4 responses received out of 5

I am facing an error when fetching sockets with the .fetchSockets function. I'm using cluster-adapter + sticky + pm2 to manage workers.

Code snippet that calls the fetchSockets() function:

const sockets= await io
          .in(`${plataform}-${client}`)
          .fetchSockets();
          
//In other line          
const sockets = (
          await io
            .in(`${plataform}-${client}-${userId}`)
            .fetchSockets()
        )[0];
```
You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:
Error: timeout reached: only 4 responses received out of 5
at Timeout._onTimeout (/opt/server/node_modules/@socket.io/cluster-adapter/dist/index.js:358:28)
at listOnTimeout (node:internal/timers:573:17)
at process.processTimers (node:internal/timers:514:7)


I've already tried everything. Furthermore, I already put a try catch around this promise

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.