koa第九章:联袂WebSocket、Nginx部署

关于端口

形如下面的代码:

const Koa = require('koa');
const app = new Koa();
// TODO: app.use(...);
app.listen(3000);

koa 通过 3000 端口响应 HTTP,我们要新加的 WebSocketServer 还能否使用 3000 端口?

答案是可以。

虽然 WebSocketServer 可以使用别的端口,但是统一端口有个最大的好处:HTTP 和 WebSocket 都使用标准的 80 和 443 端口,不需要暴露新的端口,也不需要修改防火墙规则。

那么,在 3000 端口被 koa 占用后,WebSocketServer 如何使用该端口?

实际上,3000 端口并非由 koa 监听,而是 koa 调用 Node 标准的 http 模块创建的 http.Server 监听的。koa 只是把响应函数注册到该 http.Server 中了。

类似的,WebSocketServer 也可以把自己的响应函数注册到 http.Server 中,这样,同一个端口,根据协议,可以分别由 koa 和 ws 处理:

扩展:

const http = require(‘http’);

const https = require(‘https’);

http.createServer(app.callback()).listen(3000);

https.createServer(app.callback()).listen(3001);

app.listen(…) 实际上是以下代码的语法糖。

app.callback() 返回一个适合 http.createServer() 方法的回调函数用来处理请求。您也可以使用这个回调函数将您的app挂载在 Connect/Express 应用上。

联袂WebSocket

// 服务端:app.js

const Koa = require('koa');
const WebSocket = require('ws'); 
// npm install --save ws

const app = new Koa();

let server = app.listen('3000');
console.log('应用在3000端口启动...');

const WebSocketServer = WebSocket.Server;
let wss = new WebSocketServer({ server: server });
wss.on('connection', function (ws) {
    console.log(`[SERVER] connection()`);
    ws.on('message', function (message) {
        console.log(`[SERVER] Received: ${message}`);
        ws.send(`ECHO: ${message}`, (err) => {
            if (err) {
                console.log(`[SERVER] error: ${err}`);
            }
        });
    });
});

参考资料:

1、https://github.com/websockets/ws

2、https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

3、和 ws(推荐) 一样,socket.io 也是很常用的WebSocket库。

<!--客户端-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket</title>
</head>
<body>
    <div id="app"></div>
    <script src="/js/vue.min.js"></script>
    <script src="/js/js.js"></script>
</body>
</html>

js.js

// js.js
const app = new Vue({
    el: '#app',
    data: {
        ws: null
    },
    mounted() {
        this.init();
    },
    methods: {
        init() {
            this.ws = new WebSocket('ws://localhost:3000/ws/chat');
            this.ws.onopen = () => {
                console.log(`[CLIENT] open()`);
                this.ws.send(JSON.stringify({type:'ping'}));
            };
            this.ws.onmessage = message => {
                console.log(`[CLIENT] Received: ${message.data}`);
            };
            this.ws.onclose = () => {
                console.log(`close`);
            };
            this.ws.onerror = () => {
                console.log(`error`);
            };
        }
    }
});

接着在命令行输入 npm start 或者 node app.js 即可运行 app.js,也可以在编辑器 VS Code 中执行 app.js 。 或者通过pm2让程序常驻系统。

Nginx配置反向代理

配置示例如下:

server {
    listen      80;
    server_name localhost;

    # 处理静态资源文件:
    location ^~ /static/ {
        root /path/to/ws-with-koa/static;
    }

    # 处理WebSocket连接:
    location ^~ /ws/ {
        proxy_pass         http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
    }

    # 其他所有请求:
    location / {
        proxy_pass       http://127.0.0.1:3000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

注意:如果你对普通 web 请求不想做反向代理,请把上面 location / 的规则改为如下代码,或者使用 nginx 默认规则,改完后要记得重启 nginx。

location / {
  index index.html;
}

结果演示及问题说明

我们在浏览器打开站点localhost,再打开开发人员工具,刷新一下页面,ws请求、响应如下截图:

因为我们在Nginx配置了反向代理,所以,上面的 js.js 文件里的:

this.ws = new WebSocket('ws://localhost:3000/ws/chat');

可以改为:

this.ws = new WebSocket('ws://localhost/ws/');

这样,从而只需要对外放开 80 端口就行了。

如果在 HTML 里建立不了 WebSocket 连接,可能的原因:

  • Nodejs 运行 Koa 程序本身就出错了
  • 以为用了反向代理,就用 http 协议了,如 ‘http://localhost/ws/’

更多的原因,请分别进行 Koa 调试、web浏览器调试。比起十几二十年前,现在在敲代码、调试上简直是太方便了。


已发布

分类

来自

标签: