Socket.io的集群方案

jopen 9年前發布 | 112K 次閱讀 WebSocket 開發 Socket.IO

介紹了socket.io使用Nodejs自帶的cluster與集群多進程方案。

介紹

Nodejs因其簡單方便,在服務端應用市場也開始占有一席之地,其另外一個分支--socket.io(最后跟nodejs好像又要合并了),特別適合聊天室、白板(document collabration)、在線實時計算、計數器等應用,如果要支持大容量應用,就需要使用集群技術了,下面逐一討論常見的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;           }         } }</pre>

使用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\n");   }).listen(8000); }</pre>

使用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) {});     }); }</pre>

集群后的問題

集群后帶來的主要問題就是異地服務器和多進程間的通訊問題,如果你的應用都是基于單進程顆粒的,則不需要考慮這個問題,如果你的信息在多進程則存在共享和通訊的問題,則集群后要小心處理。

官方建議的方案是將數據(事件)集中存儲(可以存儲在內存或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應用做成service并退出時可以自動重啟。當然也可以結合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

<作者 朱淦 350050183@qq.com 2015.11.8>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!