Socket.ioのクラスタスキーム


紹介する
Nodejsは簡単で便利なため、サービス側のアプリケーション市場でも一席を占め始め、もう一つの分岐点であるsocket.io(最後にnodejsと合併するようです)は、チャットルーム、ホワイトボード、オンラインリアルタイムコンピューティング、カウンタなどのアプリケーションに特に適しています.大容量アプリケーションをサポートするには、クラスタ技術が必要です.一般的なsocket.ioクラスタスキームについて説明します.
クラスタ・スキーマ
公式サイトで紹介されているシナリオにはngix逆エージェントを使用するシナリオがあります.このスキームは比較的簡単で、ビジネスコードを変更する必要がなく、iphashラウンドアルゴリズムによってユーザーが同じプロセスに割り当てられたことを保存することができる.
vi /etc/nginx/conf/nginx.conf
http { 
	upstream io_nodes {
	  ip_hash;
	  server 127.0.0.1:6001;
	  server 127.0.0.1:6002;
	  server 127.0.0.1:6003;
	  server 127.0.0.1:6004;
	}
	
        server {
          listen 3000;
          server_name io.yourhost.com;
          location / {
            #     WebSocket  ,  upgrade 
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_http_version 1.1;
            proxy_pass http://io_nodes;
          }
        }
}

Nodejs Clusterの使用
SOcketを使わなかったらioは、比較的簡単で、例は以下の通りである(原文https://nodejs.org/api/cluster.html )
npm install cluster http
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // Workers can share any TCP connection
  // In this case it is an HTTP server
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world
");   }).listen(8000); }

SOcketを使用ioシナリオ
(原文https://github.com/elad/node-cluster-socket.io)
yum install redis
npm install cluster socket.io-redis express
var express = require('express'),
    cluster = require('cluster'),
    net = require('net'),
    sio = require('socket.io'),
    sio_redis = require('socket.io-redis');

var port = 3000,
    num_processes = require('os').cpus().length;

if (cluster.isMaster) {
    var workers = [];
    var spawn = function(i) {
        workers[i] = cluster.fork();

        workers[i].on('exit', function(worker, code, signal) {
            console.log('respawning worker', i);
            spawn(i);
        });
    };
    for (var i = 0; i < num_processes; i++) {
        spawn(i);
    }
    var worker_index = function(ip, len) {
        if(ip=='::1') return 0;//for mac os x
        var s = '';
        for (var i = 0, _len = ip.length; i < _len; i++) {
            if (ip[i] !== '.') {
                s += ip[i];
            }
        }
        return Number(s) % len;
    };

    var server = net.createServer({ pauseOnConnect: true }, function(connection) {
        var worker = workers[worker_index(connection.remoteAddress, num_processes)];
        worker.send('sticky-session:connection', connection);
    }).listen(port);
} else {
    var app = new express();
    var server = app.listen(0, 'localhost'),
        io = sio(server);
    io.adapter(sio_redis({ host: 'localhost', port: 6379 }));
    process.on('message', function(message, connection) {
        if (message !== 'sticky-session:connection') {
            return;
        }
        server.emit('connection', connection);
        connection.resume();
    });
    
    //         
    io.on('connection', function (socket) {
        socket.on('message', function (data) {});
    });
}

クラスタ後の問題
クラスタ化後の主な問題は、オフサイトサーバとマルチプロセス間の通信問題です.アプリケーションが単一プロセス粒子に基づいている場合は、この問題を考慮する必要はありません.マルチプロセスで共有と通信の問題がある場合は、クラスタ化後に注意して処理してください.
公式に提案されているスキームは、データ(イベント)を集中的に格納(メモリやredisなどの永続化メディアに格納可能)し、各サービス側が集中的に格納されたデータプールからデータを取得することであり、これはすでに抽象層Socket.io-Adapterを実現しており、この抽象層はメモリを使用し、直接使用することを提案していない.ここでは、実装されているredisの例Socket.io-Redisがある(アドレス)、悪いのはredisをインストールする必要があることです.これはdemoを使用しています.
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

上記はすべてsocketプロセス間の通信です.もしあなたがsocket.からioプロセスは非socket.にメッセージを送信する.ioプロセス、例えばhttpでは、別のミドルウェアsocketが必要である.io-emitter(アドレス).例:
var io = require('socket.io-emitter')({ host: '127.0.0.1', port: 6379 });
setInterval(function(){
  io.emit('time', new Date);
}, 5000);

まとめ
本文は主にsocket.ioとnodejs clusterの統合ソリューションにより、nodejsを単一プロセスからマルチプロセスに移行させ、サービス側プログラムの容量を向上させる.Nodejsプロセスが異常に終了して自動的に再起動することを保証するために、第三者のforeverモジュールをインストールすることをお勧めします.nodejsアプリケーションをサービスにして終了したときに自動的に再起動することができます.もちろんkabbixと組み合わせて微信警報を実現することもできます(具体的には私の別の文章を参照してください).
リファレンス
Using multiple nodes
http://socket.io/docs/using-multiple-nodes/
Real Time Chat With NodeJS, Socket.io and ExpressJS
http://code.tutsplus.com/tutorials/real-time-chat-with-nodejs-socketio-and-expressjs--net-31708
sticky-session
https://github.com/indutny/sticky-session
Create a Cluster Server with Node.js and socket.IO
http://www.html5gamedevs.com/topic/12321-create-a-cluster-server-with-nodejs-and-socketio/
Scaling Socket.IO to multiple Node.js processes using cluster
http://stackoverflow.com/questions/18310635/scaling-socket-io-to-multiple-node-js-processes-using-cluster
<作者の朱淦[email protected] 2015.11.8>