由于系统是WAMP,由外包过渡过来的。在Apache设置反向代理比较不友善;且自chrome4.7以后,大部分socket请求必须基于SSL协议传输。
前提:给项目加个长连接,做个进度条。
困境:请求协议为HTTP, 如果加入SSL协议,这不仅增加一笔费用;对旧项目并不友好,php项目全是基于http的,而已Apache设置SSL估计又是折磨人。
解决:使用Apache设置反向代理,解决浏览器无法连接websocket的问题;使用localhost之前请求来解决安全性问题。
本文内容
- 项目环境技术栈
- 后端Socket服务设计
- 试错过程
- 提交方案
- 运行项目
- 参考网站
1.项目环境技术栈
- 构架时,后端代理前端压缩包文件。
- 长连接使用socket.io。
2.后端Socket服务设计
后端对外不开放,不需要使用反向代理。内容如下所示(不全):
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
const app = express();
const server = http.Server(app);
const io = socketio(server);
//加载前端页面
app.use(express.static(path.join(__dirname, 'build')));
io.on('connection', (sock) => {
console.log('Client connected');
sock.on('heartbeat', (payload) => {
payload.nodeName = name;
sock.emit('heartbeat', payload);
});
sock.on('disconnect', () => {
console.log('Socket Disconnected');
});
});
server.listen(+port, '0.0.0.0', (err) => {
console.log(`Node [${name}] listens on http://127.0.0.1:${port}.`);
});
- Socket.io和Express一起使用。
- 命令空间设计
3.试错过程
由于之前没做过,好尴尬了。看了几个小时socket.io文档直接使用了,所以踩的坑会比较多。
- 由于chrome4.7后,对安全策略进行变化 。先了解浏览器对websocket的长连接的策略。
node代理
使用node http-proxy-middleware代理socket过程,在本地(都是基本localhost)完美运行。然后上传运行代理,并开放外网端口,运行结果如下所示:
[HPM] Proxy created: / -> http://localhost:8787
[HPM] Subscribed to http-proxy events: [ 'error', 'close' ]
[HPM] Proxy created: / -> http://localhost:8787
[HPM] Subscribed to http-proxy events: [ 'error', 'close' ]
- 显示代理成功,在服务器测试socket连接正常。但使用外网访问时,如下的错误:
Uncaught (in promise) DOMException: Only secure origins are allowed. http://goo.gl/lq4gCo
- 分析过程:
- 在服务器端测试时,可以通过是因为:它们之间是localhost请求,能过浏览器安全策略。
- 服务器外网地址跟localhost在服务器在有网卡代理,localhost与外网交互,没安全策略的,它也相当于一个本地请求。
- 当在外网访问网页端时,网页在"/"链接中,地址是外网地址。外网与localhost将无法通过浏览器安全策略。
使用Apache代理过程
- 先实现一个小目标,实现一个简单的Http反向代理。
#不给别人get到你的代理
ProxyRequests off
ProxyPass http://127.0.0.1:30003/
ProxyPassReverse /
- 发现没反应,ngnix直接用的尴尬,why not ok? 查下资料,才知道要加载 proxy_module, proxy_http_module。
- 那么接下去代理,想下Apache代理比较全的帖子,我擦,完全没有提到Socket,就加入个mod_proxy_connect,说不定以后能使用SSL呢。
- 那只有去官网找,意淫术,socket的请求协议为WS,去httpd.conf搜下(找下)proxy-ws*,果然找到mod_proxy_wstunnel,就是这货了。
- 接下去的看,只能在2.4.5以上版本使用。呸,然后查下2.4.3才发行。直接用别管它。
Compatibility: |Available in httpd 2.4.5 and later - 在httpd.conf文件中,加载这模块
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
WAMP
- 有时,没什么毛病,它就是不给你运行。这个迷之原因,炸了没。别扯什么底层代理实现过程。
- 使用颜色分析过程,一般是:红色->橙色->绿色。不行的话,红色那状态会比较快,然后停到橙色。
- 使用重复启动法,重复重启。当红色停留比较久时,那就可能成功了。
4.提交方案
下面将使用Apache反向代理,使网页端与API接口变成localhost间的请求,原理图如下所示,网页端服务跟API服务将在内部服务器中:
1. 将网页端服务使用反向代理出来
- Apache 配置如下所示:
标志RewriteEngine, Rewrite*, 规则NC, N* P*
//httpd.conf
Listen 30003
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule proxy_http_module modules/mod_proxy_http.so
//httpd-vhosts.conf
RewriteEngine On
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:3030/$1 [P,L]
ProxyRequests off
ProxyPass http://127.0.0.1:3030/
ProxyPassReverse /
2.前后端分离,前端使用socket.io-client,代码如下所示:
import openSocket from 'socket.io-client';
const socket = openSocket(); //不用设置任意参数
socket.on('heartbeat', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
5.运行项目
- 访问服务器端口30003地址,其实是反向访问localhost:3000。localhost地址之间socket长连接无fuck。
-
不安全是Http协议的长连接。
6.参考网站
- chrome安全策略,解决方案,本文参考方案:https://webrtchacks.com/chrome-secure-origin-https/
- 反向代理:https://www.cnblogs.com/anruy/p/4989161.html
- 代理Http所需的模块:https://stackoverflow.com/questions/9831594/apache-and-node-js-on-the-same-server
- Apache代理讲解:http://www.apachetutor.org/admin/reverseproxies
- 标志Rewrite*: https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
- 规则N, P : https://httpd.apache.org/docs/2.4/rewrite/flags.html
- mod_proxy_wstunnel模块用于配置socket:https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
- Apache httpd.conf配置:https://stackoverflow.com/questions/29792372/apache2-websockets-reverse-proxy-on-same-url
如果socket请求跨源,一定要有SSL设置?,不太清楚