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

分享到:
关键词:Linux运维

网友留言(1 条)

  1. 农村电商
    农村电商 回复Ta
    感谢老师分享

发表评论