I am trying to integrate socket.io with Node's HTTP alongside Node's Cluster Module. Consider the reproducible example:
index.js:
let cluster = require('cluster')
let fs = require('fs')
let http = require('http')
let os = require('os')
let { Server } = require('socket.io')
const { setupMaster, setupWorker } = require("@socket.io/sticky");
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
let numCpus = os.cpus().length
if (cluster.isPrimary) {
console.log(`Master ${process.pid} is running`);
const httpServer = http.createServer();
setupMaster(httpServer, {
loadBalancingMethod: "least-connection",
});
setupPrimary();
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((req, res) => {
if (req.url.includes('admin')) {
fs.createReadStream('./admin.html').pipe(res)
}
else {
fs.createReadStream('./client.html').pipe(res)
}
});
const io = new Server(httpServer);
io.adapter(createAdapter());
setupWorker(io);
let adminNameSpace = io.of('/admin').on('connection', (socket) => {
console.log("I run once and that is okay")
console.log(socket.id)
socket.on('ADMIN_LISTEN', (_) => {
/*
This callback is not working always. According to ChatGPT, not every worker knows that there is a socket in adminNameSpace
*/
console.log("This needs to run, but it is not running.")
})
})
let clientNameSpace = io.of('/client').on('connection', socket => {
console.log("I run when I visit client")
socket.on('disconnecting', () => {
// try to disconnect
adminNameSpace.emit('ADMIN_LISTEN', 'hello')
console.log(adminNameSpace.sockets.size == 0) // it thinks that there is no socket in adminNameSpace (so it is true)
})
})
}
admin.html:
<script>
let admin = io('ws://localhost:3000/admin')
</script>
client.html:
<script>
let client = io('ws://localhost:3000/client')
</script>
Now my thought process is that there has to be 1 worker process for the rest of the workers are to handle client.html . Now, I want the workers that handle the client to publish to the admin socket.io namespace and then then it can send data to the frontend of the admin. Not sure if that defeats the purpose of clustering since while the heavy computations from the client are done by seperate workers, the results of each of them are sent over to the admin worker.
The bug in my code is (what I believe) the workers are not aware of the one worker/socket that handled the admin namespace, so the admin namespace has 0 sockets instead of 1. Therefore, when I close a tab for the client, an event in adminNameSpace is emitted but nothing is called since it sees 0 sockets.
How is it such that (either using the primary process or not) such that I have a worker dedicated to the admin and all workers are aware and can contact it through a shared socket.
Thanks