运维八一 运维八一
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)

运维八一

运维,运维!
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)
  • 操作系统

  • 域名解析

  • 公有云

  • CI&CD

  • 数据库

  • 负载均衡&反向代理

  • 存储系统

  • 容器&容器编排

  • 批量管理

  • 邮件系统

  • 监控系统

  • Web服务

    • HTTP介绍
    • nginx进阶
    • nginx优化
    • nginx容器化部署
    • Nginx反向代理问题
      • 1.1 nginx反代到nginx
      • 1.2 nginx反代到项目
      • 1.3. nginx反代配置错误页面
      • 1.4 nginx反代到阿里云
      • 1.5 nginx支持websocket代理,并保持连接超过60s不中断
        • 1.5.1 支持websocket代理
        • 1.5.2 保持连接超过60s不中断
      • 1.6 nginx 透传header到多级应用
    • nginx日志切割脚本
    • nginx log各种过滤分析
    • nginx安全问题处理
    • Nginx location Rewrite参数
    • 查看apache、nginx、mysql、php编译参数
    • Tomcat优化
    • centos下tomcat启动很慢
    • tomcat升级
    • PHP优化
    • apache+tomcat负载均衡
    • apache部署
    • apache限制地址和用户访问
    • httpd虚拟主机
    • apache配置文件参数详解
    • apache优化
    • nginx&apache&lighttpd介绍
    • Lighttpd优化
    • web程序性能动态追踪简明手册
  • 虚拟化

  • 防火墙

  • 压测

  • 文件同步

  • 私有云

  • 日志系统

  • 代码仓库&版本管理

  • 安全审计

  • 远程拨号

  • 大数据

  • 统一认证

  • 消息队列

  • Apollo

  • 运维杂记
  • Web服务
lyndon
2022-06-08
目录

Nginx反向代理问题

# 1. nginx代理问题

# 1.1 nginx反代到nginx

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

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

proxy_set_header Host xxx.com;
1

# 1.2 nginx反代到项目

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

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

underscores_in_headers on;
1

# 1.3. nginx反代配置错误页面

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

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

proxy_intercept_errors on;
1

#在server中添加error_page匹配:

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

# 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

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

# 1.5.1 支持websocket代理

在location段添加:

proxy_set_header    Upgrade    "websocket";
proxy_set_header    Connection "Upgrade";
1
2

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

Upgrade: websocket
Connection: Upgrade
1
2

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

// 状态码为101
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade
1
2
3
4

如果添加未生效,可以看下nginx http段是否有以下配置:

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}
1
2
3
4

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

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

第一种方式:

同样是在location段添加:

proxy_read_timeout  600s
proxy_send_timeout  600s
1
2

两个超时参数含义:

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)
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 1.6 nginx 透传header到多级应用

背景

调用链路:浏览器 (https://xxx:30001) --> nginx (反代到http://serviceName) --> A(http://serverName) --> B(http://serverName)--> C (https://xxx:30002) --> 回调nginx --> D

问题

  1. B在调用C时,header里只有host地址,没有端口
  2. C 在回调nginx时,应使用https协议,然而却使用了http协议,回调失败。

分析

  1. 客户端访问nginx的端口信息没有传递到C
  2. C 应用代码中根据B传递的header信息判断回调协议,默认走了http
  3. 综上,需要在nginx header中增加端口和协议信息

实现

    server {
      listen       30001 ssl;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_certificate      /etc/nginx/cert/cert.pem;
      ssl_certificate_key  /etc/nginx/cert/key.pem;

      server_name  _;
      access_log  /var/log/nginx/access.log;
      error_log   /var/log/nginx/error.log;

      location /healthz {
      access_log off;
      return 200;
      }
    location  / {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;	//透传协议
      proxy_set_header X-Forwarded-Port $server_port;	// 透传端口
      proxy_pass https://B:30013;
      }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

另外,如果存在多级代理会重写header配置时,可以自定义header头来实现

proxy_set_header My-Forwarded-Proto $scheme;
proxy_set_header My-Forwarded-Port $server_port;
1
2
上次更新: 2023/11/17, 15:47:41
nginx容器化部署
nginx日志切割脚本

← nginx容器化部署 nginx日志切割脚本→

最近更新
01
ctr和crictl显示镜像不一致
03-13
02
alpine镜像集成常用数据库客户端
03-13
03
create-cluster
02-26
更多文章>
Theme by Vdoing | Copyright © 2015-2024 op81.com
苏ICP备18041258号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式