Nodejs项目线上布置,配置Nginx反向代理WebSocket

2019-06-07 作者:web前端   |   浏览(134)

背景

什么是Nginx

前言

本文主要介绍Nodejs在ubuntu部署线上环境涉及到的知识点,包括环境配置、脚本工具、PM2应用、Ngix反向代理等等,不断完善中

推荐文章https://zhuanlan.zhihu.com/p/23778500

平时做 node 开发的时候,通过 node inspector 来进行断点调试是一个很常用的 debug 方式。但是有几个问题会导致我们的调试效率降低。

Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。

通过PM2部署线上项目

PM2是Nodejs应用用于成产过程管理的内置负载平衡器。它能够让应用持续运行,0秒重载,并能够方便的进行常见的系统管理任务。

主要特性是:
1)内建负载均衡
2)后台运行
3)0秒重载:维护升级不用停机,重新载入代码不失去连接
4)控制台检测

官方地址
官方API地址

问题一:当使用 vscode 进行断点调试时,如果应用是通过 cluster 启动的 inspector,那么每次当 worker 挂了重启后,inspector 的端口都会自增。虽然在 node8.x 版本中可以指定 inspectPort 固定调试端口,但是在 node6.x 中是不支持的。这样会导致每次 worker 重启了就得在 vscode 中重新指定调试端口。

Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好。
什么是WebSocket

部署

  • 安装PM2指令

      $ npm install pm2 -g
    
  • 配置pm2.json

      {
        "apps" : [{
          "name"        : "www",
          "cwd"         : "/data/source/",
          "script"      : "bin/www",
          "watch"       : true,
          "node_args"   : "--harmony",
          "merge_logs"  : true,
          "error_file"  : "../logs/stderr.log",
          "out_file"    : "../logs/stdout.log",
          "pid_file"   : "../pids/child.pid",
          "exec_mode"  : "cluster_mode",
          "instances"  : 0,
          "log_date_format" : "YYYY-MM-DD HH:mm Z", 
          "env": {
            "NODE_ENV": "staging"
          }
        }]
      }
    

从在上面的package.json中我们制定了很多内容
name:设置当前项目在pm2中展示的名称
cwd:指定项目源代码位置
error_file:命令行错误输出
out_file:命令行输出
evn.Node_ENV:环境变量

  • 通过pm2配置文件启动项目

    start_node_project.sh

      #!/bin/bash
      #假设工程文件压缩包如下
      tagname="project.zip"
      #删除服务器发布目录源代码
      cd /data/
      rm -rf source/*
      #解压工程压缩包到制定目录
      unzip code/${tagname} -d source/
    

    将压缩包拼上时间后缀,移动到指定目录,用于版本回退

    mv code/${tagname} code/project_date %Y%m%d%H%M.zip
    #切换到工具目录
    cd /data/deploy/
    #通过配置文件启动项目
    pm2 start pm2.json
    #查看node进程
    ps -ef|grep node

      ./start_node_project
    
  • 查看项目运行情况

      pm2 list
    

图片 1

02.png-333.2kB

  • 追踪资源运行情况

      pm2 monit
    

图片 2

03.png-16.7kB

  • 查看应用详细部署状态
    如果我们想要查看一个应用详细的运行状态,可以运行:

      pm2 descrbe {appId}
    
  • 查看日志

      pm2 logs
    
  • 重启应用

      pm2 restart {appId}
    
  • 停止运行程序

      pm2 stop pm2.json
    

问题二:当使用 devtools 调试的时候,每次调试都需要拷贝 devtools 链接到 chrome 上调试,而上面说的端口变更问题则会导致 devtools 的链接变更,除此之外,每次重新启动 inspector 也会导致 devtools 的链接变更,因为 websocket id 变了。

WebSocket协议是创建客户端和服务器端需要实时双向通讯的webapp提供了一个选择。其为HTML5的一部分,WebSocket相较于原来开发这类app的方法来说,其能使开发更加地简单。

参考文章

nodejs部署方式-pm2
nodejs线上部署小结

而把上面的两个问题简化一下就是:

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

Nginx 反向代理

Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。

之所以使用Nginx来为Nodejs做反向代理,是因为尽管Node.JS的性能不错,但处理静态事务确实不是他的专长,如:gzip编码,静态文件,HTTP缓存,SSL处理,负载平衡和反向代理及多站点代理等,都可以通过nginx来完成,从而减小node.js的负载,并通过nginx强大的缓存来节省您网站的流量从而提高网站的加载速度。

虽然node.js也有一些如http-proxy的代理模块可以实现一台服务器上面架设多个网站(每个域名映射到不同nodejs进程的端口),但这种基础性的工作,其实更应该交给ngnix来完成,下面看看一些基本操作。

  • 安装Nginx

      apt-get install nginx
    
  • 编写一个简单的配置文件/usr/local/nginx/conf/gaidu/nginx.conf

    server {
    listen 80;
    server_name h.gaidu.cn;

      access_log /data/nginx_log/h.gaidu.cn/access.log short;
      error_log /data/nginx_log/h.gaidu.cn/error.log ;
    
      if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot")
      {
          return 403;
      }
      location /  {
              proxy_pass http://10.1.2.147:9099;
              proxy_set_header  Host "h.gaidu.cn";
              proxy_set_header  X-Real-IP  $remote_addr;
              proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header  X-Forwarded-Proto  $scheme;
      }
    

    }

  • 在nginx主配置文件中引入刚创建的自定义配置文件

      cd /usr/local/nginx/conf ,
      # 打开nginx.conf, 在http 里面添加 
      include /usr/local/nginx/conf/gaidu/*
    
  • 重启nginx

      /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    
  • 通过站点访问

      h.gaidu.cn
    
  • 备注:测试ngix配置文件

      nginx -t -c /usr/local/nginx/conf/gaidu/nginx.conf
    
  • 在 vscode 中调试,在 inspector 端口变更或者 websocket id 变更后能够重连。
  • 在 devtools 中调试,在 inspector 端口变更或者 websocket id 变更后能够重连。

在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” 。在 WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处:

未完待续

解决方案

    Header。互相沟通的Header是很小的-大概只有 2 Bytes。
    Server Push。服务器的推送,服务器不再被动的接收到浏览器的请求之后才返回数据,而是在有新数据时就主动推送给浏览器。

目前业界已经有解决方案就是 chrome 插件 Node Inspector Manager(Nim),不过这个只能解决在同个 inspector 端口下的应用重启后链接更改的问题,却无法解决 cluster 启动导致的端口自增问题,除非在 Nim 中提前指定好多个端口,再者 Nim 是 chrome 上的插件,对于在 vscode 中的调试却无能为力了。

什么是noVNC

所以最佳的解决方案自然是使用 node 来做 inspector 代理,解决方案如下:

noVNC提供一种在网页上通过html5的Canvas,访问机器上vncserver提供的vnc服务,需要做tcp到websocket的转化,才能在html5中显示出来。网页就是一个客户端,类似win下面的vncviewer,只是此时填的不是裸露的vnc服务的ip port,而是由noVNC提供的websockets的代理,在noVNC代理服务器上要配置每个vnc服务,noVNC提供一个标识,去反向代理所配置的vnc服务。
WebSocket代理

对于第一个问题,在 vscode 上,它是会自己去调用 /json 接口获取最新的 websocket id,然后使用新的 websocket id 连接到 node inspector 服务上。因此解决方法就是实现一个 tcp 代理功能做数据转发即可。

要将客户端和服务器之间的连接从HTTP / 1.1转换为WebSocket,使用HTTP / 1.1中提供的协议切换机制。

对于第二个问题,由于 devtools 是不会自动去获取新的 websocket id 的,所以我们需要做动态替换,所以解决方案就是代理服务去 /json 获取 websocket id,然后在 websocket 握手的时候将 websocket id 进行动态替换到请求头上。

然而有一个微妙之处:由于“Upgrade”是一个 逐跳的头,它不会从客户端传递到代理服务器。使用正向代理,客户可以使用该CONNECT 方法来规避这个问题。但是,这不适用于反向代理,因为客户端不知道任何代理服务器,并且需要在代理服务器上进行特殊处理。

画了一张流程图:

从版本1.3.13开始,nginx实现了特殊的操作模式,如果代理服务器返回了代码101(交换协议)的响应,客户端和代理服务器之间建立隧道,客户端通过请求中的“Upgrade”请求头。

图片 3

如上所述,包括“Upgrade”和“Connection”的逐跳标题不会从客户端传递到代理服务器,因此为了让代理服务器知道客户端将协议切换到WebSocket的意图,这些标题必须明确地通过:

实现步骤

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

一、Tcp 代理

    server {
        listen 80; #修改监听的端口
        server_name _;
        location / {
            proxy_pass  #修改为需要被反向代理的WebSocket的IP和端口号
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }

首先,先实现一个 tcp 代理的功能,其实很简单,就是通过 node 的 net 模块创建一个代理端口的 Tcp Server,然后当有连接过来的时候,再创建一个连接到目标端口即可,然后就可以进行数据的转发了。

默认情况下,如果代理服务器在60秒内没有传输任何数据,连接将被关闭。这个超时可以通过proxy_read_timeout指令来增加 。或者,代理服务器可以配置为周期性地发送WebSocket ping帧来重置超时并检查连接是否仍然存在。

简易的实现如下:

实例--以代理noVNC为例
实验环境

const net = require('net');
const proxyPort = 9229;
const forwardPort = 5858;

net.createServer(client => {
 const server = net.connect({
  host: '127.0.0.1',
  port: forwardPort,
 }, () => {
  client.pipe(server).pipe(client);
 });
 // 如果真要应用到业务中,还得监听一下错误/关闭事件,在连接关闭时即时销毁创建的 socket。
}).listen(proxyPort);

    已经安装好noVNC的CentOS7虚拟机[安装说明](叫它vnc-server),IP地址(NAT模式)为192.168.204.10
    最小化安装的CentOS7虚拟机(叫它proxy-server),IP地址(NAT模式)为192.168.204.133,IP地址(仅主机模式)为192.168.50.128
    Windows7虚拟机(叫它client),IP地址(仅主机模式)为192.168.50.129
    首先vnc-server和client的网络是隔离的,现在让proxy-server反向代理vnc-server的noVNC服务,达到的效果是client访问proxy-server就可以访问到vnc-server的noNVC服务。

上面实现了比较简单的一个代理服务,通过 pipe 方法将两个服务的数据连通起来。client 有数据的时候会被转发到 server 中,server 有数据的时候也会转发到 client 中。

配置proxy-server

当完成这个 Tcp 代理功能之后,就已经可以实现 vscode 的调试需求了,在 vscode 中项目下 launch.json 中指定端口为代理端口,在 configurations 中添加配置

proxy-server上关闭防火墙

{
 "type": "node",
 "request": "attach",
 "name": "Attach",
 "protocol": "inspector",
 "restart": true,
 "port": 9229
}

setenforce 0
systemctl stop firewalld
systemctl disable firewalld

那么当应用重启,或者更换 inspect 的端口,vscode 都能自动重新通过代理端口 attach 到你的应用。

proxy-server上安装nginx

二、获取 websocketId

yum install -y epel*
yum install -y nginx
systemctl start nginx
systemctl enable nginx

这一步开始,就是为了解决 devtools 链接不变的情况下能够重新 attach 的问题了,在启动 node inspector server 的时候,inspector 服务还提供了一个 /json 的 http 接口用来获取 websocket id。

proxy-server上编辑Nginx的配置文件

这个就相当简单了,直接发个 http 请求到目标端口的 /json,就可以获取到数据了:

vim /etc/nginx/nginx.conf

[ { description: 'node.js instance',
  devtoolsFrontendUrl: '...',
  faviconUrl: 'https://nodejs.org/static/favicon.ico',
  id: 'e7ef6313-1ce0-4b07-b690-d3cf5274d8b0',
  title: '/Users/wanghx/Workspace/larva-team/vscode-log/index.js',
  type: 'node',
  url: 'file:///Users/wanghx/Workspace/larva-team/vscode-log/index.js',
  webSocketDebuggerUrl: 'ws://127.0.0.1:5858/e7ef6313-1ce0-4b07-b690-d3cf5274d8b0' } ]

在http区块添加如下内容

上面数据中的 id 字段,就是我们需要的 websocket id 了。

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

三、Inspector 代理

server {
    listen 8080; #修改监听的端口
    server_name _;
    location / {
        proxy_pass ;  #修改为需要被反向代理的WebSocket的IP和端口号
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

拿到了 websocket id 后,就可以在 tcp 代理中做 websocket id 的动态替换了,首先我们需要固定链接,因此先定一个代理链接,比如我的代理服务端口是 9229,那么 chrome devtools 的代理链接就是:

图片 4

chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/__ws_proxy__

重启nginx服务

上面除了最后面的 ws=127.0.0.1:9229/__ws_proxy__其他都是固定的,而最后这个也一眼就可以看出来是 websocket 的链接。其中__ws_proxy__则是用来占位的,用于在 chrome devtools 向这个代理链接发起 websocket 握手请求的时候,将 __ws_proxy__替换成 websocket id 然后转发到 node 的 inspector 服务上。

systemctl restart nginx

对上面的 tcp 代理中的 pipe逻辑的代码做一些小修改即可。

在client上测试

const through = require('through2');
...

client
   .pipe(through.obj((chunk, enc, done) => {
    if (chunk[0] === 0x47 && chunk[1] === 0x45 && chunk[2] === 0x54) {
     const content = chunk.toString();
     if (content.includes('__ws_proxy__')) {
      return done(null, Buffer.from(content.replace('__ws_proxy__', websocketId)));
     }
    }
    done(null, chunk);
   }))
   .pipe(server)
   .pipe(client);
...

Windows7网卡配置

通过 through2 创建一个 transform 流来对传输的数据进行一下更改。

图片 5

简单判断一下 chunk 的头三个字节是否为GET,如果是 GET 说明这可能是个 http 请求,也就可能是 websocket 的协议升级请求。把请求头打印出来就是这个样子的:

通过火狐浏览器访问proxy-server仅主机网卡的web服务
可以看到,通过nginx已经成功代理了WebSocket!

GET /__ws_proxy__ HTTP/1.1
Host: 127.0.0.1:9229
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: chrome-devtools://devtools
Sec-WebSocket-Version: 13
...

图片 6

然后将其中的路径/__ws_proxy替换成对应的 websocketId,然后转发到 node 的 inspector server 上,即可完成 websocket 的握手,接下来的 websocket 通信就不需要对数据做处理,直接转发即可。

Linux公社的RSS地址:

接下来就算各种重启应用,或者更换 inspector 的端口,都不需要更换 debug 链接,只需要再 inspector server 重启的时候,在下图的弹窗中

本文永久更新链接地址

图片 7

图片 8

点击一下 Reconnect DevTools 即可恢复 debug。

最后

上面的详细代码可以在下面的 git 中找到:

  • Tcp 代理:
  • Inspector 代理:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

  • 抛弃Nginx使用nodejs做反向代理服务器
  • 教你如何使用node.js制作代理服务器
  • 基于node.js的快速开发透明代理
  • Linux安装NodeJs并配合Nginx实现反向代理
  • Node.js站点使用Nginx作反向代理时配置GZip压缩的教程
  • 使用nodejs中httpProxy代理时候出现404异常的解决方法
  • 腾讯云(ubuntu)下安装 nodejs 实现 Nginx 反向代理服务器
  • 详解node.js搭建代理服务器请求数据

本文由www.bifa365365.com发布于web前端,转载请注明出处:Nodejs项目线上布置,配置Nginx反向代理WebSocket

关键词: www.bifa3653