Socket.io的集群方案

Stella981
• 阅读 657

介绍

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;
          }
        }
}

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

使用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应用做成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

http://stackoverflow.com/questions/18310635/scaling-socket-io-to-multiple-node-js-processes-using-cluster

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

Socket.io的集群方案
有书一起读

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
MongoDB 分片管理(一)检查集群状态
一、检查集群状态1.1使用sh.status()查看集群摘要信息1、使用sh.status()可以查看分片信息、数据库信息、集合信息sh.status()如果数据块较多时,使用sh.status(true)又是输出会很多,就不会截断,要使用如下查看2、tooman
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这