websoket.io 高并发 实现

标签: websocket | 发表时间:2017-03-02 16:28 | 作者:红叶舞秋山
出处:https://segmentfault.com/blogs

用websocket搭个聊天室实时互动小游戏,确实非常方便,但在线人数多了 就没那么简单了。一到300人在线就开始掉了。后来经过调整 终于好多了。以下是改进的方案记录:

调整websocket.io的传输方式

websoket.io Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用。支持主流浏览器,多种平台,多种传输模式。
关于传输模式主要有两种1.polly 轮询模式, 2.websocket 模式。
默认为先轮询握手连接后再升级为websocket。 轮询的效率肯定是低下的,修改配置参数直接用websokect 的方式传输。发现很效果差很多,满意!

  var io = require('socket.io')({ "transports":['websocket', 'polling']})

判断传输方式的源码位置:

https://github.com/socketio/e...

使用命名空间的功能

可以区分不同的命名空间来特定针对性的发消息,对于一些不是全局需要接受的消息就加上命名空间,可以极大的节约资源的传输。

  //创建 server命名空间
var ServerIo = io.of('/server').on('connection', function(socket){

 socket.on('ready',function(roomId,data) {
   pub.publish(roomId, JSON.stringify({
          "event": 'ready',
          "data": '',
          "namespace" : '/user'
        }))
 })

 socket.on('button-start',function(id){
        pub.publish(id, JSON.stringify({
          "event": 'button-start',
          "data": '',
          "namespace" : '/user'
        }));
 })
 
 //针对namespace发送消息
 io.of(namespace).emit('message', message)

nginx的负载均衡配置

通过nodeJS的process模块,可以实现多进程运行程序,最大限制的提高cpu的利用率。

多进程启动:

  var fork = require('child_process').fork;
var cupNum = require('os').cpus().length,
    workerArr = [],
  connectNum = 0 ;

for (var i = 0; i < cupNum; i++) {
    workerArr.push(fork('./shake_server.js', [8000 + i]));

    process.on('uncaughtException', function(e) {
          console.log('捕获到进程异常:',e);
        });
}

然后通过nginx做负载

  upstream io_nodes {
 ip_hash;
 server 127.0.0.1:8000;
 server 127.0.0.1:8001;
 server 127.0.0.1:8003;
 server 127.0.0.1:8002;
 }

    server {
        listen       8080;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location /html {
            alias   /Users/snail/Documents/myworks/weihuodong/shake;
            index  index.html index.htm;
         }
        location / {
            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;
            proxy_redirect off;
            keepalive_timeout 300S 
       }

一些系统参数的调整:

1.修改系统参数:

http.Agent
官网说明:
agent.maxSockets
By default set to 5. Determines how many concurrent sockets the agent can have open per host.
(为了http请求能复用connection连接,Nodejs在http.Agent创建了一个默认大小为5的连接池)
修改后如下:require("http").globalAgent.maxSockets = Infinity; 如果使用WebSocke.io

2.ulimit 调整

心跳检测 掉线检测

如果出现自动掉线可调整nginx的各种timeout参数,有太多配置参数,具体根据需要去查文档配置

如: keepalive_timeout 的调整,默认75秒

还可以通过程序代码判断,掉线会触发disconcent 事件,监听它 自动再创建socket连接即可。
心跳检查为定时发送一次消息,保持连接状态。

内存数据共享

nodeJs的进程间通信是没问题的,但要共享数据还是用redis来的简单有效。
直接创建一个redisClient实例来用即可。

  var client = redisClient(35050, '127.0.0.1')

  var redisObj = {
      get:function(id,cb){
          client.hgetall(id, function (err, obj) {
              if(err){
                console.log(err)
              }else{
                 cb(obj)
              }
          })
      }, 
      set:function(id,o){
        if(typeof o === 'undefined' || typeof Object.keys(o)[0] == 'undefined'){
          return
        }
       var key = Object.keys(o)[0]
       var value =JSON.stringify(o[key])
       client.hset(id,key,value)
      },
      del:function(id,uid){
        if(id && uid){
         client.hdel(id,uid)
        }
      },
      flushdb:function(roomId){
        client.del(roomId)
      }

  }

socket在各个进程间也是不共享的,那么我们可以利用redis的订阅发布系统实现。

详细看代码注释:

  var redis = require("redis");
var sub = redis.createClient(35050, '127.0.0.1'), pub = redis.createClient(35050, '127.0.0.1');
var msg_count = 0;
//订阅事件的会时候触发 subscribe ,回调包含两个参数,分别为订阅的频道和总订阅数量
sub.on("subscribe", function (channel, count) {
   console.log('监听到订阅事件',channel, count)
});
//在pub的时候会触发 message事件,我们的所有业务处理基本就是靠监听它了,通知socket发布消息
sub.on("message", function (channel, message) {
   console.log('监听到发布事件')
   console.log("sub channel " + channel + ": " + message);
   // socket.to(channel).emit('nice game', "let's play a game");
   socket.of(channel).emit('message', message)

   msg_count += 1;
   if (msg_count === 3) {
       sub.unsubscribe()
       sub.quit()
       pub.quit();
   }
});

//添加三个订阅
sub.subscribe("channel0");
sub.subscribe("channel1");
sub.subscribe("channel2");

//触发频道1的订阅者
setInterval(function(){
    pub.publish("channel1", "I am message to chanle1")
},3e3)

//触发频道2的订阅者
setInterval(function(){
  pub.publish("channel2", "I am message to chanle2")
},3e3)

经测试一台服务器1、2千用户没什么压力
测试的话 用webSocket bench 不好用.
(完)

相关 [websoket io 并发] 推荐:

高性能网络编程5--IO复用与并发编程

- - CSDN博客云计算推荐文章
对于服务器的并发处理能力,我们需要的是:每一毫秒服务器都能及时处理这一毫秒内收到的数百个不同TCP连接上的报文,与此同时,可能服务器上还有数以十万计的最近几秒没有收发任何报文的相对不活跃连接. 同时处理多个并行发生事件的连接,简称为并发;同时处理万计、十万计的连接,则是高并发. 服务器的并发编程所追求的就是处理的并发连接数目无限大,同时维持着高效率使用CPU等资源,直至物理资源首先耗尽.

物理IO与逻辑IO

- - 操作系统 - ITeye博客
IO性能对于一个系统的影响是至关重要的. 一个系统经过多项优化以后,瓶颈往往落在数据库;而数据库经过多种优化以后,瓶颈最终会落到IO. 而IO性能的发展,明显落后于CPU的发展. Memchached也好,NoSql也好,这些流行技术的背后都在直接或者间接地回避IO瓶颈,从而提高系统性能. 上图层次比较多,但总的就是三部分.

linux异步IO浅析

- Sepher - kouu&#39;s home
知道异步IO已经很久了,但是直到最近,才真正用它来解决一下实际问题(在一个CPU密集型的应用中,有一些需要处理的数据可能放在磁盘上. 预先知道这些数据的位置,所以预先发起异步IO读请求. 等到真正需要用到这些数据的时候,再等待异步IO完成. 使用了异步IO,在发起IO请求到实际使用数据这段时间内,程序还可以继续做其他事情).

java nio和io的比较

- - 互联网 - ITeye博客
第一部分:简单介绍NIO.     服务器在合理时间内处理大量客户机的请求的能力取决于服务器使用I/O流的效率,同时为成百上千的客户提供服务的服务器必须能并发的使用I/O服务.     用Java语言写的服务器,由于其线程与客户机之比几乎是一比一,因而易受到大量线程开销的影响,其结果是即导致性能问题,又缺乏伸缩性.

C++之文件IO操作流

- Nanqi - 博客园-首页原创精华区
  前两节介绍了C++的IO流类库,标准设备IO操作流中部分预定义流对象的成员函数以及IO格式控制. 那今天我将继续介绍关于C++中的流操作内容——文件IO操作流fstream. 并会着重讲解C++是如何对文件进行操作的.   文件指存放在外部介质上的数据的集合. 大家都知道操作系统是以文件为单位来对数据进行管理的.

异步IO一定更好吗?

- Wolf - CNode社区
在长林的文章《nodejs异步IO的实现》中提到,NodeJS通过libeio来实现IO操作的异步化,而libeio采用多线程的方式来模拟异步操作. 这里我需要强调一个观点,异步IO虽然是NodeJS一个非常重要的特点,但异步IO并不总是最好的,其他语言也一样. 在我的磁盘上有2个文件,我希望在一个程序里读取这2个文件,每次输出一个字符.

linux AIO (异步IO) 那点事儿

- zffl - CNode社区
这时候进程至少会阻塞10次,而这可能会导致其他的上千个用户请求得不到处理,这当然是不能接受的.. Linux AIO 早就被提上议程,目前比较知名的有 Glibc 的 AIO   与 Kernel Native AIO. 我们用Glibc 的AIO 做个小实验,写一个简单的程序:异步方式读取一个文件,并注册异步回调函数:.

MySQL数据库的IO操作

- - haohtml's blog
         淘宝丁奇分享的PPT:MySQL数据库的IO操作,详细分享了四块的内容,并且告诉大家如何调整MySQL数据库IO操作相关的参数,给出了详细的选择策略,现替其整理成文章分享与此. 4.影响io行为的一些参数和选择策略. 一个简单的查询 select * from t where id>=(  select id from t where k1=100 limit 100000,1) limit 2;.

定位IO瓶颈的一些方法

- - Linux - 操作系统 - ITeye博客
IO瓶颈往往是我们可能会忽略的地方(我们常会看top、free、netstat等等,但经常会忽略IO的负载情况),今天给大家详细分享一下如何确认一台服务器的IO负载是否到达了瓶颈,以及可能优化、定位的点. 先来看一台典型的IO密集型服务器的cpu统计图:. 可以看到,CPU总使用率不高,平均1.3%,max到5.6%,虽然大部分都耗在了iowait上,但才百分之五左右,应该还没到瓶颈吧.

hadoop的IO和MapReduce优化参数

- - CSDN博客系统运维推荐文章
           在MapReduce执行过程中,特别是Shuffle阶段,尽量使用内存缓冲区存储数据,减少磁盘溢写次数;同时在作业执行过程中增加并行度,都能够显著提高系统性能,这也是配置优化的一个重要依据.            下面分别介绍I/O属性和MapReduce属性这两个类的部分属性,并指明其优化方向.