nginx开启websocket代理功能
使用Nginx代理WebSocket的时候,客户端与服务器握手成功后,如果在60秒内没有数据交互,就会自动断开连接。因为Nginx默认的断开链接时间为60秒,为保持长连接,可有两种解决方法。
第一种修改nginx配置
[root@web conf]# cat test.shnne.com.conf server { listen 80; server_name test.shnne.com; location / { proxy_pass http://192.168.1.10:88; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #websocket proxy_read_timeout 600s; #这条是保持10分钟才断开,方式默认60s就自动断开 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } }
下面依次介绍各参数作用:
location /shnne/websocket { # 反向代理转发地址 proxy_pass http://test.shnne.com/websocket; # 超时时间 proxy_read_timeout 600; # Websocket Support proxy_http_version 1.1; proxy_set_header upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # 其他设置 # 代理是否支持重定向 proxy_redirect off; #远端真实地址 proxy_set_header X-Real-IP $remote_addr; # HTTP请求的主机域名 proxy_set_header Host $host; # 反向代理之后转发之前的ip地址 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # nginx代理 proxy_set_header X-Nginx-Proxy true; }
升级http1.1到 websocket协议。另外有一个特别值得注意的地方是,如果websocket服务器在收到websocket握手包,查看Origin信息与所在域信息不符的话。会直接拒绝服务。这点很坑,我花了接近半天的时间来找为什么连不上的原因,最后竟然发现是Origin 必须和请求地址在一个域,不然会被拒绝访问并且返回403.
【补充】
NGINX支持WebSocket。
对于NGINX将升级请求从客户端发送到后台服务器,必须明确设置Upgrade和Connection标题。
这也算是上面情况所非常常用的场景。
HTTP的Upgrade协议头机制用于将连接从HTTP连接升级到WebSocket连接,Upgrade机制使用了Upgrade协议头和Connection协议头。
为了让Nginx可以将来自客户端的Upgrade请求发送到后端服务器,Upgrade和Connection的头信息必须被显式的设置。
【注意】
在nginx的配置文件中,如果当前模块中没有proxy_set_header的设置,则会从上级别继承配置。
继承顺序为:http, server, location。
如果在下一层使用proxy_set_header修改了header的值,则所有的header值都可能会发生变化,之前继承的所有配置将会被丢弃。
所以,尽量在同一个地方进行proxy_set_header,否则可能会有别的问题。
按照上述方法设置好后,我们可以发现,如果在10分钟之内没有数据交互的话,websocket连接就会自动断开,所以这种方式还是有点问题,如果我页面停留时间超过十分钟而且又没有数据交互的话,连接还是会断开的,所以需要同时结合第二种方法.
第二种:在Nginx延时基础上,前端在超时时间内做心跳检测
var socket; // 心跳检测,每隔一段时间检测连接状态,如果处于连接中,就像Server主动发送消息,来重置Server段与客户端的最大连接时间,如果已经断开,发起重连 var heartCheck = { // 9分钟发起一次心跳,比Server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间 timeout: 550000, serverTimeoutObj: null, reset: function () { clearTimeout(this.serverTimeoutObj); return this; }, start: function () { this.serverTimeoutObj = setInterval(function () { if (socket.readyState == 1) { console.log("连接状态,发送消息保持连接"); socket.send("ping"); // 如果获取到消息,说明连接正常,重置心跳检测 heartCheck.reset().start(); } else { console.log("断开连接,尝试重连"); connect(); } }, this.timeout) } }; function connect() { if (typeof WebSocket == "undefined") { console.log("不支持WebSocket!"); } else { console.log("支持WebSocket!"); } socket = new WebSocket("ws://47.99.57.113:1234/club/websocket"); // 打开事件 socket.onopen = function () { heartCheck.reset().start(); console.log("socket 已打开"); }; // 获得消息事件 socket.onmessage = function (msg) { heartCheck.reset().start(); console.log(msg.data); }; // 关闭事件 socket.onclose = function () { console.log("Socket 已关闭") }; // 发生错误 socket.onerror = function () { console.log("Socket 发生了错误") }; } function close() { socket.close(); }
另外一个检测
// websocket连接 var websocket_connected_count = 0; var onclose_connected_count = 0; function newWebSocket() { var websocket = null; // 判断当前环境是否支持websocket if(window.WebSocket){ if(!websocket){ var ws_url ="wss://"+domain+"/updatewebsocket"; websocket = new WebSocket(ws_url); } }else{ Tip("not support websocket"); } // 连接成功建立的回调方法 websocket.onopen = function(e) { heartCheck.reset().start(); // 成功建立连接后,重置心跳检测 Tip("connected successfully") } // 连接发生错误,连接错误时会继续尝试发起连接(尝试5次) websocket.onerror = function() { console.log("onerror连接发生错误") websocket_connected_count++; if(websocket_connected_count <= 5){ newWebSocket() } } // 接受到消息的回调方法 websocket.onmessage = function(e) { console.log("接受到消息了") heartCheck.reset().start(); // 如果获取到消息,说明连接是正常的,重置心跳检测 var message = e.data; if(message){ //执行接收到消息的操作,一般是刷新UI } } // 接受到服务端关闭连接时的回调方法 websocket.onclose = function() { Tip("onclose断开连接"); } // 监听窗口事件,当窗口关闭时,主动断开websocket连接,防止连接没断开就关闭窗口,server端报错 window.onbeforeunload = function() { websocket.close(); } // 心跳检测, 每隔一段时间检测连接状态,如果处于连接中,就向server端主动发送消息,来重置server端与客户端的最大连接时间,如果已经断开了,发起重连。 var heartCheck = { timeout: 55000, // 9分钟发一次心跳,比server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间。 serverTimeoutObj: null, reset: function() { clearInterval(this.serverTimeoutObj); return this; }, start: function() { var self = this; this.serverTimeoutObj = setInterval(function() { if(websocket.readyState == 1){ console.log("连接状态,发送消息保持连接"); websocket.send("ping"); // heartCheck.reset().start(); // 如果获取到消息,说明连接是正常的,重置心跳检测 } else { console.log("断开状态,尝试重连"); newWebSocket(); } }, this.timeout) } } }
参考:https://nginx.org/en/docs/http/websocket.html
<< 上一篇
下一篇 >>
网友留言(1 条)