복붙노트

[REDIS] 여러 Node.js를에 스케일링 Socket.IO는 클러스터를 사용하여 처리

REDIS

여러 Node.js를에 스케일링 Socket.IO는 클러스터를 사용하여 처리

이것으로 내 머리를 찢어 ... 누군가는 Node.js를의 클러스터 모듈 양산 다수의 "노동자"프로세스에 Socket.IO를 확장 할 수있게되었다?

내가 네 작업자 프로세스 (의사)에서 다음을 말할 수 있습니다 :

// on the server
var express = require('express');
var server = express();
var socket = require('socket.io');
var io = socket.listen(server);

// socket.io
io.set('store', new socket.RedisStore);

// set-up connections...
io.sockets.on('connection', function(socket) {

  socket.on('join', function(rooms) {
    rooms.forEach(function(room) {
      socket.join(room);
    });
  });

  socket.on('leave', function(rooms) {
    rooms.forEach(function(room) {
      socket.leave(room);
    });
  });

});

// Emit a message every second
function send() {
  io.sockets.in('room').emit('data', 'howdy');
}

setInterval(send, 1000);

그리고 브라우저에 ...

// on the client
socket = io.connect();
socket.emit('join', ['room']);

socket.on('data', function(data){
  console.log(data);
});

문제 : 모든 초, 나는 네 개의 메시지, 메시지를 보내는 인해 네 개의 개별 작업자 프로세스를 수신하고 있습니다.

단지 어떻게 메시지를 확인 할을 한 번 보내진다?

해결법

  1. ==============================

    1.편집 :에서 Socket.IO 1.0 이상은, 오히려 여러 레디 스 클라이언트와 저장소를 설정하는 것보다, 단순한 레디 스 어댑터 모듈은 이제 사용할 수 있습니다.

    편집 :에서 Socket.IO 1.0 이상은, 오히려 여러 레디 스 클라이언트와 저장소를 설정하는 것보다, 단순한 레디 스 어댑터 모듈은 이제 사용할 수 있습니다.

    var io = require('socket.io')(3000);
    var redis = require('socket.io-redis');
    io.adapter(redis({ host: 'localhost', port: 6379 }));
    

    아래의 예는 더 같을 것이다 :

    var cluster = require('cluster');
    var os = require('os');
    
    if (cluster.isMaster) {
      // we create a HTTP server, but we do not use listen
      // that way, we have a socket.io server that doesn't accept connections
      var server = require('http').createServer();
      var io = require('socket.io').listen(server);
      var redis = require('socket.io-redis');
    
      io.adapter(redis({ host: 'localhost', port: 6379 }));
    
      setInterval(function() {
        // all workers will receive this in Redis, and emit
        io.emit('data', 'payload');
      }, 1000);
    
      for (var i = 0; i < os.cpus().length; i++) {
        cluster.fork();
      }
    
      cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
      }); 
    }
    
    if (cluster.isWorker) {
      var express = require('express');
      var app = express();
    
      var http = require('http');
      var server = http.createServer(app);
      var io = require('socket.io').listen(server);
      var redis = require('socket.io-redis');
    
      io.adapter(redis({ host: 'localhost', port: 6379 }));
      io.on('connection', function(socket) {
        socket.emit('data', 'connected to worker: ' + cluster.worker.id);
      });
    
      app.listen(80);
    }
    

    당신은 요구 사항이 다른 Socket.IO 프로세스에 게시 할 수 있지만, socket.io - 레디 스 대신 소켓 연결 자체를 사용 socket.io - 이미 터에 동의하지 않는 마스터 노드가 있다면.

    당신은 문제가 스케일링가있는 경우, 노드 DEBUG와 응용 프로그램 = *를 실행합니다. Socket.IO는 지금 구현 디버그도 레디 스 어댑터 디버그 메시지를 출력 할 것이다. 출력 예 :

    socket.io:server initializing namespace / +0ms
    socket.io:server creating engine.io instance with opts {"path":"/socket.io"} +2ms
    socket.io:server attaching client serving req handler +2ms
    socket.io-parser encoding packet {"type":2,"data":["event","payload"],"nsp":"/"} +0ms
    socket.io-parser encoded {"type":2,"data":["event","payload"],"nsp":"/"} as 2["event","payload"] +1ms
    socket.io-redis ignore same uid +0ms
    

    마스터와 아이 모두 두 디스플레이에 동일한 파서 메시지를 처리하는 경우, 응용 프로그램이 제대로 확장됩니다.

    단일 노동자로부터 방출되는 경우 설치에 문제가 안됩니다. 당신이 할 수있는 응용 프로그램을 질문으로 당신이 네 노동자로부터 방출되는 일을하는지, 그리고 레디 스는 공개 / 등록에 의한, 메시지는 네 번 중복,하지만 기록되지 않습니다. 여기 레디 스가하는 일의 간단한 다이어그램입니다 :

    Client  <--  Worker 1 emit -->  Redis
    Client  <--  Worker 2  <----------|
    Client  <--  Worker 3  <----------|
    Client  <--  Worker 4  <----------|
    

    당신이 볼 수 있듯이 당신이 노동자로부터 방출 될 때, 그것은 레디 스에 발광을 게시 할 것이며, 그것은 레디 스 데이터베이스에 가입 한 다른 근로자에서 미러링됩니다. 이것은 또한 당신이 동일한 인스턴스에 연결된 여러 개의 소켓 서버를 사용할 수 있다는 것을 의미하고, 하나 개의 서버에 발광는 연결된 모든 서버에서 발사됩니다.

    클러스터로 할 때 클라이언트 연결, 당신의 네 노동자 중 하나, 모든 넷에 연결됩니다. 그것은 또한 당신은 클라이언트에 한 번 표시됩니다 그 노동자에서 방출 것을 의미한다. 그래서 그래, 애플리케이션은 확장이지만, 당신이 그것을하고있는 방법, 당신은 네 근로자 방출하고, 당신은 하나의 노동자에 네 번 호출하는 것처럼 레디 스 데이터베이스를하고있다. 클라이언트가 실제로 소켓 인스턴스의 모든 4 개의 연결 한 경우, 그들은 여섯 메시지 번째가 아닌 사를 받게 될 것입니다.

    소켓 처리의 유형은 할 겁니다 응용 프로그램의 유형에 따라 달라집니다. 개별적으로 클라이언트를 처리하기 위하여려고하는 경우에, 당신은, 아무 문제가 없어야하기 때문에 연결 이벤트 것 하나의 클라이언트 당 하나의 작업자 만 불. 당신이 전역 "하트 비트"를 필요로하는 경우에, 당신은 마스터 과정에서 소켓 핸들러를 가질 수있다. 노동자 때 마스터 프로세스 다이 다이 때문에, 당신은 마스터 프로세스에서 다른 곳으로 연결 부하를 상쇄하고, 아이들이 연결을 처리 할 수 ​​있도록해야한다. 다음은 그 예이다 :

    var cluster = require('cluster');
    var os = require('os');
    
    if (cluster.isMaster) {
      // we create a HTTP server, but we do not use listen
      // that way, we have a socket.io server that doesn't accept connections
      var server = require('http').createServer();
      var io = require('socket.io').listen(server);
    
      var RedisStore = require('socket.io/lib/stores/redis');
      var redis = require('socket.io/node_modules/redis');
    
      io.set('store', new RedisStore({
        redisPub: redis.createClient(),
        redisSub: redis.createClient(),
        redisClient: redis.createClient()
      }));
    
      setInterval(function() {
        // all workers will receive this in Redis, and emit
        io.sockets.emit('data', 'payload');
      }, 1000);
    
      for (var i = 0; i < os.cpus().length; i++) {
        cluster.fork();
      }
    
      cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
      }); 
    }
    
    if (cluster.isWorker) {
      var express = require('express');
      var app = express();
    
      var http = require('http');
      var server = http.createServer(app);
      var io = require('socket.io').listen(server);
    
      var RedisStore = require('socket.io/lib/stores/redis');
      var redis = require('socket.io/node_modules/redis');
    
      io.set('store', new RedisStore({
        redisPub: redis.createClient(),
        redisSub: redis.createClient(),
        redisClient: redis.createClient()
      }));
    
      io.sockets.on('connection', function(socket) {
        socket.emit('data', 'connected to worker: ' + cluster.worker.id);
      });
    
      app.listen(80);
    }
    

    예에서, 다섯 개 Socket.IO 인스턴스 하나 인 마스터, 4 인 아이들이있다. 그 과정에 어떤 연결 오버 헤드가 없다 ()하므로 마스터 서버는 호출을 듣지 않았다. 마스터 과정에 발광을 호출 할 경우, 그것은 레디 스에 게시되며, 네 개의 작업자 프로세스는 클라이언트에 발광을 수행합니다. 근로자가 죽는 경우 직원이 오프셋 연결 부하, 그리고, 메인 애플리케이션 로직은 마스터의 손길이 닿지 않은 것입니다.

    그 과정에서 발광을 유발하는 것처럼 참고도 공간이나 실내에서 레디 스, 모두를 방출을 가진 것을 다른 작업자 프로세스에 의해 처리됩니다. 즉, 당신은 당신의 발광라는 것처럼 직원이이 같은 행동을 할 것 인 동안, 고객에게 데이터를 보내드립니다 최초의 노동자의 소켓 개의 발광 ()를 호출 한 레디 스 인스턴스 두 Socket.IO 인스턴스가있는 경우 그 노동자.

  2. ==============================

    2.마스터가 당신의 하트 비트 (아래 예) 처리하거나 다른 내부 포트 (또한 위쪽 V1.3에서 WebSocket을 지원)의 nginx와로드 밸런싱을 여러 프로세스를 시작하자.

    마스터가 당신의 하트 비트 (아래 예) 처리하거나 다른 내부 포트 (또한 위쪽 V1.3에서 WebSocket을 지원)의 nginx와로드 밸런싱을 여러 프로세스를 시작하자.

    마스터와 클러스터

    // on the server
    var express = require('express');
    var server = express();
    var socket = require('socket.io');
    var io = socket.listen(server);
    var cluster = require('cluster');
    var numCPUs = require('os').cpus().length;
    
    // socket.io
    io.set('store', new socket.RedisStore);
    
    // set-up connections...
    io.sockets.on('connection', function(socket) {
        socket.on('join', function(rooms) {
            rooms.forEach(function(room) {
                socket.join(room);
            });
        });
    
        socket.on('leave', function(rooms) {
            rooms.forEach(function(room) {
                socket.leave(room);
            });
        });
    
    });
    
    if (cluster.isMaster) {
        // Fork workers.
        for (var i = 0; i < numCPUs; i++) {
            cluster.fork();
        }
    
        // Emit a message every second
        function send() {
            console.log('howdy');
            io.sockets.in('room').emit('data', 'howdy');
        }
    
        setInterval(send, 1000);
    
    
        cluster.on('exit', function(worker, code, signal) {
            console.log('worker ' + worker.process.pid + ' died');
        }); 
    }
    
  3. ==============================

    3.이것은 실제로 Socket.IO는 확장에 성공처럼 보인다. 당신은 상관없이 연결 할 일이있는 서버의, 그 방에있는 모든 소켓으로 이동 한 서버에서 메시지를 기대.

    이것은 실제로 Socket.IO는 확장에 성공처럼 보인다. 당신은 상관없이 연결 할 일이있는 서버의, 그 방에있는 모든 소켓으로 이동 한 서버에서 메시지를 기대.

    가장 좋은 방법은 메시지 각 초를 보내는 하나 개의 마스터 과정을하는 것입니다. 당신은 예를 들어, cluster.isMaster 경우를 실행하여이 작업을 수행 할 수 있습니다.

  4. ==============================

    4.프로세스 간 통신 클러스터와 socket.io 1.4.5 작업을하는 것만으로는 충분하지 않습니다. 웹 소켓 모드를 강제하는 것은 필수입니다. 웹 소켓 핸드 쉐이크 Node.js를에서 참조, Socket.IO 및 클러스터가 작동하지

    프로세스 간 통신 클러스터와 socket.io 1.4.5 작업을하는 것만으로는 충분하지 않습니다. 웹 소켓 모드를 강제하는 것은 필수입니다. 웹 소켓 핸드 쉐이크 Node.js를에서 참조, Socket.IO 및 클러스터가 작동하지

  5. from https://stackoverflow.com/questions/18310635/scaling-socket-io-to-multiple-node-js-processes-using-cluster by cc-by-sa and MIT license