1. nginx代理问题

1.1 nginx反代到nginx

问题: 使用域名可以访问,使用IP报404

解决: 在location段proxy_pass参数上面加上一行:

proxy_set_header Host xxx.com;

1.2 nginx反代到项目

问题: location匹配到规则之后,无法从一个完整的url跳到另一个完整的url

解决: 在nginx配置文件的http段添加一行参数:

underscores_in_headers on;

1.3. nginx反代配置错误页面

问题: 无法捕捉到proxy_pass后的状态码(4xx、5xx)进行处理

解决:
#在server中location段proxy_pass参数上面添加一行

proxy_intercept_errors on;

#在server中添加error_page匹配:

error_page   403 404 500 502 503 504  /error.html;
location = /error.html {
    root html/serverError;
    }

1.4 nginx反代到阿里云

问题: nginx反代proxy_pass到函数计算服务时,由于传入的header中host是前端域名,不是函数计算绑定的域名,而且会把upstream中server域名解析为IP,阿里云无法识别。

解决:
#在server中location段中修改proxy_set_header参数,手动设置header host,传入函数计算的域名

proxy_set_header Host xxx.com;

1.5 nginx支持websocket代理,并保持连接超过60s不中断

1.5.1 支持websocket代理

在location段添加:

proxy_set_header    Upgrade    "websocket";
proxy_set_header    Connection "Upgrade";

**原理:**关键部分在于HTTP的请求中多了如下头部:

Upgrade: websocket
Connection: Upgrade

这两个字段表示请求服务器升级协议为WebSocket。服务器处理完请求后,响应如下报文:

// 状态码为101
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade

1.5.2 保持连接超过60s不中断

利用nginx代理websocket的时候,发现客户端和服务器握手成功后,如果在60s时间内没有数据交互,连接就会自动断开,为了保持长连接,可以采取来两种方式.

第一种方式:

同样是在location段添加:

proxy_read_timeout  600s
proxy_send_timeout  600s

两个超时参数含义:

proxy_read_timeout语法:默认值 60s 上下文 http server location 说明 该指令设置与代理服务器的读超时时间。它决定了nginx会等待多长时间来获得请求的响应。 这个时间不是获得整个response的时间,而是两次reading操作的时间。
proxy_send_timeout语法:默认值 60s 上下文 http server location 说明 这个指定设置了发送请求给upstream服务器的超时时间。超时设置不是为了整个发送期间,而是在两次write操作期间。 如果超时后,upstream没有收到新的数据,nginx会关闭连接

如果在10分钟之内没有数据交互的话,websocket连接就会自动断开,所以这种方式还是有点问题,如果我页面停留时间超过十分钟而且又没有数据交互的话,连接还是会断开的,所以需要同时结合第二种方法.

第二种方式:

在nginx延长超时时间的基础上,前端在超时时间内发心跳包,刷新再读时间,前端具体实现见如下代码(此处代码包含了前端整个websocket的实现过程,其中加粗重点标注了发心跳包的内容):

// 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(){
            clearTimeout(this.timeoutObj);
            clearTimeout(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)
        }
    }

}