📜 ⬆️ ⬇️

Cluster + EXPRESS + socket.io without REDIS

I really liked the socket.io library. With it, you can easily implement realtime applications. She herself chooses the protocol depending on the browser, if necessary, creates WebSocket.

This is, of course, all good, but you should not forget that Node.js works in one thread. For this we have a great tool Cluset.

Before me there was a question, is it possible to make applications with clustering, but not to use REDIS for this? Also for convenience we use EXPRESS.

Let's create a simple example (so far without a cluster): Example from official documentation:
')
var app = require('express')(); var server = require('http').Server(app); var io = require('socket.io')(server); server.listen(3038); app.get('/', function (req, res) { res.sendfile(__dirname + '/index.html'); }); io.on('connection', function (socket) { socket.emit('news', { hello: 'world' }); socket.on('my other event', function (data) { console.log(data); }); }); 

So far, nothing complicated, everything is clear, we hang the server on port 3038 with express and soket.io. Client code will be unchanged:

 <html> <head> <title>Test socket.io</title> <script src="socket.io.js"></script> <script> var connect_error_count = 0; var socket = io.connect('http://localhost:3038/', { 'reconnectionDelay': 10 // defaults to 500 } ); socket.on('connect_error', function(){ console.log('Connection Failed'); //  5  ,    connect_error_count++; if (connect_error_count>=5){ socket.disconnect(); console.log("stop reconection"); } }); socket.on('reconnect', function(){ console.log('reconnect'); connect_error_count=0; }); socket.on('news', function (data) { console.log(data); }); </script> </head> <body> </body> </html> 

A few explanations on the client code. Create a socket object with settings (port 3038, connection timeout 10 ms). In case of connection errors more than 5 times, disable the work of the socket, if less, then reset the counter. And also connect to the room news.

The next step is to create a cluster. Since Since a socket cannot be created on one port, then each new worker will create socket.io on a new port:

 var cluster = require('cluster');// cluster var cpuCount = require('os').cpus().length;//   var io = []; //   worker'     if (cluster.isMaster) { for (var i = 0; i < cpuCount; i += 1) { var worker = cluster.fork(); } } //  if (cluster.isWorker) { var worker_id = cluster.worker.id; var express = require('express'); var app = express(); var server = require('http').Server(app); io[worker_id] = require('socket.io')(server); server.listen(3030+worker_id); io[worker_id].on('connection', function (socket) { console.log( socket.id ); console.log( "WORKER ID :"+worker_id ); socket.emit('news', { hello: 'world' }); socket.on('my other event', function (data) { console.log(data); }); }); var app_express = express(); app_express.listen(8081); app_express.use(express.static('public'));//   (.  ) app_express.get('/', function (request, response) { response.send('Hello from Worker '+worker_id); console.log( '------' ); }); app_express.get('/get_port', function (request, response) { response.send(3030+worker_id); console.log( 'get_port' ); }); } app_express.get('/api', function (request, response) { response.setHeader('Content-Type', 'application/json'); var id = request.param('id'); var msg = request.param('msg'); var port = request.param('port'); var JSON_DATA = { "worker_id":worker_id ,"id":id ,"msg":msg ,"port":port }; }); 

Schematically show how it all looks:

image

When we open localhost: 8081 in the browser, the cluster serves us on a worker by its own rule. We have a call:

 app_express.get('/api', function (request, response){...} 

In this call, when opening localhost: 8081 / api? Msg = test & port = 3032 & id = 100500 we want to send a message with msg = teest to the client connected via port 3032 id = 100500.

The easiest way is to create a client and open socket.io on a specific port:

 var http = require('http'); var client = http.createClient(3032 , "localhost"); request = client.request(); request.on('response', function( res ) { res.on('data', function( data ) { console.log( data ); } ); } ); request.end(); 

But for example, we need to send a broadcast message, and we do not know the port on which the client is served. The problem is this: we do not know for sure that when opening localhost: 8081 / api? Msg = test & port = 3032 & id = 100500, we will be served on worker 2.

image

The figure shows that when sending a broadcast message with worker1, we are not able to send socket.io 2, socket.io 3, socket.io 4, socket.io directly to clients ...

To solve this problem, we can use the methods in the cluster. In cluster, we can send messages from master → worker and from worker → master. For clarity, we sketch this:

image

An example of working with sending master → worker and worker → master is shown below:

 //   var cluster = require('cluster'); var io = []; var cpuCount = require('os').cpus().length; var workers = []; if (cluster.isMaster) { // Count the machine's CPUs // Create a worker for each CPU for (var i = 0; i < cpuCount; i += 1) { var worker = cluster.fork(); //   workera worker.on('message', function(data) { //  worker'  for (var j in workers) {workers[j].send(data);} }); //  worker   workers.push(worker); } } if (cluster.isWorker) { //------------------------------------------------------------------------------------// var worker_id = cluster.worker.id; var express = require('express'); var app = express(); var server = require('http').Server(app); io[worker_id] = require('socket.io')(server); server.listen(3030+worker_id); io[worker_id].on('connection', function (socket) { console.log( socket.id ); console.log( "WORKER ID :"+worker_id ); socket.emit('news', { hello: 'world' }); socket.on('my other event', function (data) { console.log(data); }); }); //------------------------------------------------------------------------------------// var app_express = express(); app_express.listen(8081); app_express.use(express.static('public'));//   app_express.get('/', function (request, response) { response.send('Hello from Worker '+worker_id); console.log( '------' ); }); app_express.get('/get_port', function (request, response) { response.send(3030+worker_id); console.log( 'get_port' ); }); app_express.get('/api', function (request, response) { //process.send("----------------------"); response.setHeader('Content-Type', 'application/json'); var id = request.param('id'); var msg = request.param('msg'); var JSON_DATA = { "worker_id":worker_id ,"id":id ,"msg":msg }; io[port-3030].to(msg.id).emit('news', msg.msg); response.send( JSON.stringify(JSON_DATA) ); //    process.send(JSON_DATA); }); //   worker// process.on('message', function(msg){ console.log(worker_id); console.log(msg.id); console.log(msg.msg); io[worker_id].to(msg.id).emit('news', msg.msg); }); } 

I hope this article will be useful for beginners who want to get acquainted with the cluster and with communication between master → worker and worker → master. A brief overview of socket.io. And most importantly, redis is not used.

Source: https://habr.com/ru/post/326172/


All Articles