连接多个客户端时,socket.io应用程序失败

时间:2013-12-11 00:15:56

标签: node.js socket.io

我为其中一个网站编写了一项功能,可以在其他用户向其发送消息时向用户发送提醒。如果我只为少数用户启用它,该功能可用于开发和生产。然后,当为几千个并发用户启用时,该功能将失败(推送警报停止发送)(服务器保持运行状态,因此它不会简单地死亡)。从日志中可以看出,在'send_msg'事件中找不到套接字。

我还注意到,当我尝试直接从浏览器访问http://www.mydomain.com:4086/socket.io/socket.io.js时,有时它会加载,有时却不加载,我不知道原因是什么。我在下面发布的代码中是否有任何错误?在过去的三天里,我一直在抨击这个。

编辑:看起来将ulimit从1024提升到65535会使应用程序在开始失败前几分钟购买“警告 - 客户端没有握手客户端应该重新连接”和“警告 - websocket解析器错误:操作码10没有处理程序“再次出现错误。我确信我一次没有任何接近65535个并发连接的地方。有人可以解释我是否走在正确的轨道上?

服务器代码(server.js):

var io = require('socket.io').listen(4086);
var clients = {};

io.sockets.on('connection', function (socket) {
    socket.on('login', function (data) {
        if (typeof data.id_user !== 'undefined' && data.id_user > 0) {
            socket.set('id_user', data.id_user, function() {});
            clients[data.id_user] = socket.id;
        }
    });

    socket.on('send_msg', function (data) {
        if (typeof io.sockets.sockets[clients[data.id_sendee]] !== 'undefined') {
            var sendeeSocket = io.sockets.sockets[clients[data.id_sendee]];
            sendeeSocket.emit('newalert', {
                sender_profile: data.sender_profile,
                msg: data.msg,
                send_to: data.send_to });
        }
    });

    socket.on('disconnect', function() {
        socket.get('id_user', function (err, id_user) {
            if (typeof id_user !== 'undefined' && id_user > 0) {
                delete clients[id_user];
            }
        });
    });
});

客户代码:

<html>
<head>
<script src="http://www.mydomain.com:4086/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('http://www.mydomain.com:4086');

    socket.emit('login', { id_user: <?=$idUser?> });

    socket.on('newalert', function (data) {
        $("#msg_stream").hide();
        $("#stream_sender_profile").html(data.sender_profile);
        $("#stream_msg").text(data.msg);
        $("#stream_send_to").html(data.send_to);
        $("#msg_stream").fadeIn();
    });
</script>
</head>
<body>

.......

<script>
    // Alert stream
    $(document).ready(function() {
        $("#submit_btn").click(function() {
            var msg = $("#message").val();
            if (msg) {
                if (msg.length > 255) {
                    msg = msg.substr(0, 255) + '...';
                }
                socket.emit('send_msg', {
                    sender_profile: '<a href="/profile/view/<?=$idUser?>" class="stream_dismiss"><?=$username?></a>',
                    id_sendee: <?=$idSendee?>,
                    msg: msg,
                    send_to: '<?=isset($streamSendTo) ? $streamSendTo : '';?>' });
            }
        });
    });
</script>
</body>
</html>

1 个答案:

答案 0 :(得分:0)

我最终设法解决了我的连接问题,我的解决方案主要围绕最佳服务器配置。

Node.js代码(index.js)

&#13;
&#13;
var arguments = process.argv.slice(2);
if( ! arguments[0]) {
    console.log("Error: Missing port.");
    process.exit(1);
}
var serverPort = arguments[0];
var redisHost = '127.0.0.1';
var redisPort = 6379;

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var redis = require('socket.io-redis');
io.adapter(redis({ host: redisHost, port: redisPort }));

io.on('connection', function(socket){
  console.log(socket.id + ' user connected!');

  socket.on('disconnect', function(){
    console.log(socket.id + ' user disconnected');
  });
});

http.listen(serverPort, function(){
  console.log('listening on localhost:' + serverPort);
});
&#13;
&#13;
&#13;

与我在问题中发布的代码不同,我不再将消息提交给node.js,而是通过我的web应用程序直接与socket.io接口。这个决定与性能无关,所以可以随意进一步构建脚本。

配置您的服务器

增加打开文件限制(包括您为节点进程指定的任何用户)(/etc/security/limits.conf):

*                soft    nofile          999999
*                hard    nofile          999999
root             soft    nofile          999999
root             hard    nofile          999999

调整服务器网络:

请参阅:https://engineering.gosquared.com/optimising-nginx-node-js-and-networking-for-heavy-workloads

/etc/sysctl.conf中:

net.ipv4.ip_local_port_range='1024 65000'
net.ipv4.tcp_tw_reuse='1'
net.ipv4.tcp_fin_timeout='15'
net.core.netdev_max_backlog='4096'
net.core.rmem_max='16777216'
net.core.somaxconn='4096'
net.core.wmem_max='16777216'
net.ipv4.tcp_max_syn_backlog='20480'
net.ipv4.tcp_max_tw_buckets='400000'
net.ipv4.tcp_no_metrics_save='1'
net.ipv4.tcp_rmem='4096 87380 16777216'
net.ipv4.tcp_syn_retries='2'
net.ipv4.tcp_synack_retries='2'
net.ipv4.tcp_wmem='4096 65536 16777216'
vm.min_free_kbytes='65536'

/etc/nginx/nginx.conf(用于群集和粘性会话):

http {
    ...
    upstream io_nodes {
       # Enabled sticky sessions
       ip_hash;
       server 127.0.0.1:3000;
       server 127.0.0.1:3001;
       server 127.0.0.1:3002;
       server 127.0.0.1:3003;
    }

    server {
        ...

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

我尝试将群集模块用于节点但是我无法将其用于在不同群集节点上的用户之间正确传递消息,无论出于何种原因,尽管使用Redis作为共享媒体,因此我手动配置群集和粘性会话Nginx的。然后我用我在nginx.conf中指定的端口运行index.js:

node index.js 3000
node index.js 3001
node index.js 3002
node index.js 3003