介绍
Nodejs因其简单方便,在服务端应用市场也开始占有一席之地,其另外一个分支--socket.io(最后跟nodejs好像又要合并了),特别适合聊天室、白板(document collabration)、在线实时计算、计数器等应用,如果要支持大容量应用,就需要使用集群技术了,下面逐一讨论常见的socket.io集群方案。
集群方案
在官网介绍的方案有使用ngix反向代理方案。这种方案比较简单不需要修改业务代码可以直接布署,通过iphash轮调算法保存用户分配到同一个进程。
1vi /etc/nginx/conf/nginx.conf 2http { 3 upstream io_nodes { 4 ip_hash; 5 server 127.0.0.1:6001; 6 server 127.0.0.1:6002; 7 server 127.0.0.1:6003; 8 server 127.0.0.1:6004; 9 } 10 11 server { 12 listen 3000; 13 server_name io.yourhost.com; 14 location / { 15 #为支持转发WebSocket数据,加上upgrade头 16 proxy_set_header Upgrade $http_upgrade; 17 proxy_set_header Connection "upgrade"; 18 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 19 proxy_set_header Host $host; 20 proxy_http_version 1.1; 21 proxy_pass http://io_nodes; 22 } 23 } 24}
使用Nodejs Cluster
如果没有使用socket.io,则比较简单,例子如下 (原文 https://nodejs.org/api/cluster.html )
1npm install cluster http 2 3var cluster = require('cluster'); 4var http = require('http'); 5var numCPUs = require('os').cpus().length; 6 7if (cluster.isMaster) { 8 // Fork workers. 9 for (var i = 0; i < numCPUs; i++) { 10 cluster.fork(); 11 } 12 13 cluster.on('exit', function(worker, code, signal) { 14 console.log('worker ' + worker.process.pid + ' died'); 15 }); 16} else { 17 // Workers can share any TCP connection 18 // In this case it is an HTTP server 19 http.createServer(function(req, res) { 20 res.writeHead(200); 21 res.end("hello world\n"); 22 }).listen(8000); 23}
使用socket.io方案
(原文 https://github.com/elad/node-cluster-socket.io)
1yum install redis 2npm install cluster socket.io-redis express 3 4var express = require('express'), 5 cluster = require('cluster'), 6 net = require('net'), 7 sio = require('socket.io'), 8 sio_redis = require('socket.io-redis'); 9 10var port = 3000, 11 num_processes = require('os').cpus().length; 12 13if (cluster.isMaster) { 14 var workers = []; 15 var spawn = function(i) { 16 workers[i] = cluster.fork(); 17 18 workers[i].on('exit', function(worker, code, signal) { 19 console.log('respawning worker', i); 20 spawn(i); 21 }); 22 }; 23 for (var i = 0; i < num_processes; i++) { 24 spawn(i); 25 } 26 var worker_index = function(ip, len) { 27 if(ip=='::1') return 0;//for mac os x 28 var s = ''; 29 for (var i = 0, _len = ip.length; i < _len; i++) { 30 if (ip[i] !== '.') { 31 s += ip[i]; 32 } 33 } 34 return Number(s) % len; 35 }; 36 37 var server = net.createServer({ pauseOnConnect: true }, function(connection) { 38 var worker = workers[worker_index(connection.remoteAddress, num_processes)]; 39 worker.send('sticky-session:connection', connection); 40 }).listen(port); 41} else { 42 var app = new express(); 43 var server = app.listen(0, 'localhost'), 44 io = sio(server); 45 io.adapter(sio_redis({ host: 'localhost', port: 6379 })); 46 process.on('message', function(message, connection) { 47 if (message !== 'sticky-session:connection') { 48 return; 49 } 50 server.emit('connection', connection); 51 connection.resume(); 52 }); 53 54 //这里写你的业务代码 55 io.on('connection', function (socket) { 56 socket.on('message', function (data) {}); 57 }); 58}
集群后的问题
集群后带来的主要问题就是异地服务器和多进程间的通讯问题,如果你的应用都是基于单进程颗粒的,则不需要考虑这个问题,如果你的信息在多进程则存在共享和通讯的问题,则集群后要小心处理。
官方建议的方案是将数据(事件)集中存储(可以存储在内存或redis等持久化介质里),然后各服务端从集中存储的数据池里获取数据,其已经实现了一个抽象层 Socket.io-Adapter,这个抽象层使用内存,不建议直接使用,这里有人实现的redis的例子Socket.io-Redis(地址),坏处是你需要先安装redis。这是使用demo
1var io = require('socket.io')(3000); 2var redis = require('socket.io-redis'); 3io.adapter(redis({ host: 'localhost', port: 6379 }));
上面说的都是socket进程间的通讯,如果你要从socket.io进程发消息给非socket.io进程,如http,则需要另外一个中间件socket.io-emitter(地址)。例子:
1var io = require('socket.io-emitter')({ host: '127.0.0.1', port: 6379 }); 2setInterval(function(){ 3 io.emit('time', new Date); 4}, 5000);
总结
本文主要讨论了socket.io与nodejs cluster的整合解决方案,让nodejs从单进程转成多进程,提高服务端程序的容量。为保证nodejs进程异常退出自动重启,建议安装第三方的forever模块,可以将nodejs应用做成service并退出时可以自动重启。当然也可以结合kabbix实现微信报警(具体实现参考我的另外一文章)。
updated:如果要支持https,最简单的就是用nginx作个反向代理。
参考
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
<作者 朱淦 350050183@qq.com 2015.11.8>

有书一起读