使用Socket.io搭建WS服务并通过Nginx反向代理

Nodejs、Express、Socket.io、Nginx

使用Express框架和Socket.io库将websocket搭建起来,并监听同一个端口,参考官方例子[1]就可以,非常简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

server.listen(80);
// WARNING: app.listen(80) will NOT work here!

app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});

io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});

Nginx同时作为Web Server和WebSocket Reverse Proxy

在配置Nginx的时候,遇到了很多的问题,并且在网上现在找不到一篇对这个问题讲解的非常清楚的文档。
比如:

  1. Nginx是否支持http/websocket在同一个端口。答案是可以的。
  2. Nginx支持支持代理socket.io。 结论:支持。
  3. var socket = io(); 初始化的时候,io()要不要传入地址参数。 结论:不需要传入参数。
  4. Nginx配置的URL匹配规则是什么。

全部代码请跳转Github.

经过试验和参考Nginx的文档[2],Nginx配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 8080;
server_name localhost;

location / {
root D:\\Projects\\ws-proxy\\docroot;
index index.html index.htm;
}

location ^~/socket.io {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8885;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

为什么nginx上配置的Location必须是固定的,通过调试socket.io的机制就清楚了,见下。如果将nginx location中的规则修改成其他的,前端将无法连接socket.io服务。

除了Socket.io之外,是否还要其他选择?

可以使用ws库来搭建一个websocket服务。这个和socket.io的区别在于socket.io做了额外的很多工作,比如某些浏览器上不支持websocket,那么socket.io会通过轮询的方式来模拟websocket.

如果使用nodejs, 可以参考Nginx官方的文档[3]

配置Nginx

申请SSL证书

先申请SSL证书,有付费的,有免费的,一般来说,如果购买了腾讯云/阿里云的域名都可以申请到免费证书。也可以到Let’s Encrypt网站上申请。这个网站的服务是由互联网安全研究工作组ISRG提供的。

不过我使用Caddy软件一键自动部署(代理)和自动申请证书。不过从使用体验上来看,似乎Caddy要稍微性能要慢一些。

Caddy自动申请的证书存放的位置[5]在:
~/.caddy/acme/acme-v01.api.letsencrypt.org/sites/xxx.xxx(域名)/

所以,如果有Caddy已经自动申请了证书,那么nginx可以直接使用该证书。

配置nginx, 开启HTTPS

默认nginx的配置目录在/etc/nginx下面。

可以通过DigitalOcean提供的免费Nginx的配置工具[6]来生成配置文件,这个网站非常棒,提供了很丰富的配置。生成出来的配置按照Site进行了分组。比如我的域名node-nj.typedefine.cn, 我只需要打开sites-available下面的node-nj.typedefine.cn.conf配置进行查看就行了。

nginx依然通过nginx.conf来加载配置,但是nginx.conf->sites-enabled->sites-available。基本上只需要关注sites-available中每个站点的独立的配置就可以了。

如果需要将SSL证书共用,也可以配置到Global Config中。具体使用参考说明。

配置nginx到Systemd中

新增nginx.service到/etc/systemd/system目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

重新加载systemd
systemctl daemon-reload
启动服务
systemctl start nginx
查看服务状态
systemctl status nginx
查看日志
sudo journalctl -u nginx

配置反向代理:

按照如下配置即可[4]

1
2
3
4
5
6
7
location /wsapp/ {
proxy_pass http://wsbackend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}

完整的站点配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name node-nj.typedefine.cn;

# SSL
ssl_certificate /root/.caddy/acme/acme-v02.api.letsencrypt.org/sites/node-nj.typedefine.cn/node-nj.typedefine.cn.crt;
ssl_certificate_key /root/.caddy/acme/acme-v02.api.letsencrypt.org/sites/node-nj.typedefine.cn/node-nj.typedefine.cn.key;

# security
include nginxconfig.io/security.conf;

location / {
root /root/docroot;
index index.html index.htm index.php;
}

location ^~/ray {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8442;
}

# handle .php
location ~ \.php$ {
include nginxconfig.io/php_fastcgi.conf;
}

# additional config
include nginxconfig.io/general.conf;
}


  1. 1.https://socket.io/docs/#Using-with-Express
  2. 2.https://www.nginx.com/blog/nginx-nodejs-websockets-socketio/
  3. 3.https://www.nginx.com/blog/websocket-nginx/
  4. 4.http://nginx.org/en/docs/http/websocket.html
  5. 5.https://www.cmsky.com/caddy-proxy-shell/
  6. 6.https://www.digitalocean.com/community/tools/nginx