node pm2 + nginx 部署websocket项目解决方案--超详细总结

经过了几天捣鼓,加上请教大牛,将自己走过的这个坑总结如下。以下代码亲测有效,工程使用的node express框架

需求分析:

      因为nodejs是单线程,在实际项目运行时,一旦遇到错误,会导致整个系统崩溃,因此需要使用node的多进程模型,比如简易使用的pm2进程管理工具。在常规的http服务中,pm2直接启动工程即可(下文附上简单pm2介绍),可是一旦server中集成了socket.io服务就会导致ws通道建立失败,为什么要用socket.io呢,因为我的项目需求是前端调用后端算法,后端需要计算几分钟至几个小时的时间,然后再将计算结果显示到前端(这个就不重点介绍了)。因此我们需要解决这种问题,让socket.io充分利用多核,且保证连接成功。

为什么会出现socket.io连接失败呢?

       因为pm2进程在分发请求的阶段采用了某种算法的均衡,如round-robin或者其他hash方式(但不是iphash),因此在socket.io客户端连接建立阶段发送的多个xhr请求,会被pm2定位到不同的worker进程中。前文中提到每个xhr请求都会携带sid字段标识当前连接,因此当一个携带sid字段的请求被pm2定位到另一个与该连接无关的worker时,就会造成请求失败,返回{"code":1,"message":"Session ID unknown"}错误;即使前三次xhr握手成功,进入websocket连接升级阶段,负责侦听update事件的worker也往往不是之前的那个worder,因此导致websocket连接建立失败。

      简单来说就是,客户端多次请求的服务端进程不是同一个进程才导致的ws连接无法成功建立。

解决方案:nginx反向代理+iphash


1、首先nginx安装即常用命令

官方下载地址:https://nginx.org/en/download.html

Linux下安装:

先解压:tar zxvf nginx-1.15.8.tar.gz
进入解压后目录:./configure
               make && make install

 

nginx -v 查看是否安装成功

编辑配置文件,centos系统默认在/etc/nginx/,一定要使用root权限才能编辑

[xxxx@powermonster ~]$ nginx -v
nginx version: nginx/1.12.1
[xxxx@powermonster ~]$ sudo -i
[sudo] password for xxxx:
[root@powermonster ~]# cd /etc/nginx/
[root@powermonster nginx]# ls
conf.d          koi-utf  mime.types  nginx.conf   uwsgi_params
fastcgi_params  koi-win  modules     scgi_params  win-utf
[root@powermonster nginx]# vim nginx.conf

配置文件修改如下:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
upstream io_nodes {
      ip_hash;
      server 127.0.0.1:3131;
      server 127.0.0.1:3132;
      server 127.0.0.1:3133;
      server 127.0.0.1:3134;
    }

    server {
        listen 3000; //端口随意设置,只要是外网能访问的且不被占用的即可
        server_name 12.34.56.78; //这是你自己的服务器IP地址,我这随意写了一个
        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;
           }
        }
}

常用命令:

测试配置文件是否正确:
[root@powermonster]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

[root@powermonster]#nginx -s reload            # 重新载入配置文件
[root@powermonster]#nginx -s reopen            # 重启 Nginx
[root@powermonster]#nginx -s stop              # 停止 Nginx

若nginx安装成功,则http://12.34.56.78:3000/能够访问,页面有nginx版本提示。


2、pm2安装配置

pm2 是一个带有负载均衡功能的Node应用的进程管理器。可以把你的独立代码利用全部的服务器上的所有CPU,并保证进程永远都活着,0秒的重载。

pm2的主要特性:

1、内建负载均衡(使用Node cluster 集群模块) 
2、后台运行 
3、0秒停机重载 
4、具有Ubuntu和CentOS 的启动脚本 
5、停止不稳定的进程(避免无限循环) 
6、控制台检测 
7、提供 HTTP API 
8、远程控制和实时的接口API ( Nodejs 模块,允许和PM2进程管理器交互 )

pm2的安装:

npm install -g pm2

接下来新建一个pm2.json,将该文件放置到你要运行的工程根目录下,如图所示:

pm2.json内容如下:

  "apps": [
    {
      "name": "ws",
      "script": "./app.js",
      "env": {
        "NODE_ENV": "development"
      },
      "env_production": {
        "NODE_ENV": "production"
      },
      "instances": 4,
      "exec_mode": "cluster",
      "max_restarts" : 3,
      "restart_delay" : 5000,
      "log_date_format" : "YYYY-MM-DD HH:mm Z",
      "combine_logs" : true
    }
  ]
}


3、接下来要改动你的工程代码了,app.js以及调用socket的HTML文件

app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var app = express();
var server = require('http').Server(app),
port = 3131 + parseInt(process.env.NODE_APP_INSTANCE),
io = require('socket.io')(server);

io
  .on('connection', function(socket) {
      socket.on('disconnect', function() {
          console.log('/: disconnect-------->')
      });

      socket.on('handle_event', function() {
          socket.emit('return_event', '/: '+port);
          console.log('/: '+port)
      });
  });

io.of('/ws')
  .on('connection', function(socket) {
    socket.on('disconnect', function() {
        console.log('disconnect-------->')
    });

    socket.on('handle_event', function() {
        socket.emit('return_event', port);
    });
});
app.set('port', port);
server.listen(port);
module.exports = app;

例如在index.html中调用socket,则代码如下:

 <script>
        var btn = document.getElementById('btn1');
        btn.addEventListener('click',function(){
            var socket = io.connect('http://12.34.56.78/ws',{
                reconnection: false
            });
            var data={a:1};
            socket.on('connect',function(){
                // 发送数据
                socket.emit('handle_event',data);
                //接收数据
                socket.on('return_event',function(d){
                    console.log(d);
                });

            });

            socket.on('error',function(err){
                console.log(err);
            })
        });
    </script>

4、最后就是pm2运行node工程啦

话不多说,上图,内含常用pm2指令

看到上面的界面,表明你的部署正常,可以http://12.34.56.78:3000/访问页面啦

初级前端,还请路过的大牛指教。。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值