记一次蛮有意思的Nginx线上排错

现象

  • 给两台服务器配置一台nginx用于负载均衡高可用,但是通过nginx请求数据的时候,发现每次打到第一台的时候,报错,第二台没问题(通过分别反向代理发现哪一台有问题的)。
  • 但是,单独去调用每一台服务器的接口,都可以正常拿到数据。

排查过程

  • 第一步:看看是不是网络问题
    • 通过在nginx上ping和telnet port看看网络和端口通不通,看一下两台服务和nginx的防火墙是否都开通。
    • 结果都是通的。
  • 第二步:查看是否是机器原因
    • 将负载均衡改成单台的反向代理。结果是第一台依旧不可以,第二台没问题。
    • 大概率就是跟第一台的某些配置有关系。
  • 第三步:抓包确认数据是否到达,以及差异
    • 通过分别在服务器上安装Wireshark抓包,发现从nginx过来的请求,除了postman的token不一样,其他均一样,但是两台机器的response不同,第一台永远输出error信息,第二台永远输出成功信息。看一下第一台的error信息,大概是和权限认证相关。
    • 猜测可能是跟第一台的权限认证相关。
  • 第四步:查看nginx的error.log
    • 没看出什么有用的信息。
  • 第五步:求助公司大佬
    • 推测是不是会话保持的原因,于是修改nginx的配置文件,其中upstream的分配算法修改为ip_hash。
    • 之前抓包也发现了通过nginx转发的报文,connection:close,而直接调用的报文的connection=keep-alive,所以有理由怀疑是因为未保持链接造成认证失败。
    • 成功解决问题,测试的时候,每次都成功取到数据。

后记

  • 五分钟后,在回家的路上,我认为我错了,这个问题并没有解决,因为ip_hash算法是保证同一个ip映射到同一个后端,做会话保持,我测试的时候,是本机,同一个ip,存在侥幸情况,我映射到第二台上,所以每次请求都是成功的。

  • 今天继续验证的时候,换了一台机器发送请求,果然失败了,事后诸葛亮一下,发现三个铁证:

    • 之前试过单独代理第一台也是不成功的,所以不太可能是会话保持的原因
    • 通过nginx转发的时候,两台机器的报文都是connection:close,所以如果是因为会话保持的原因,不可能一台通,一台不通。
    • 修改了转发算法为ip_hash后,通过抓包发现,报文依旧是connection:close,并且nginx不能在响应头部添加Keep-Alive,nginx采用的是http1.0协议,因此没有这个可能。关于nginx不负责的会话保持机制,见Nginx会话保持
  • 在反复查看报错信息之后,公司大佬建议我把nginx的http转成tcp试试,结果成了,经过反复测试,终于确认这次是真的成功了。

http{
    
    
    upstream tomcat_pool{
    
    
        #server tomcat地址:端口号 weight表示权值,权值越大,被分配的几率越大;
        server 192.168.80.22:8080 weight=4 max_fails=2 fail_timeout=30s;
        server 192.168.80.22:8081 weight=4 max_fails=2 fail_timeout=30s;
    }
    server {
    
    
        listen       80;
        server_name  tomcat_pool;
        location / {
    
    
            #root   html;
            #index  index.html index.htm;
            proxy_pass http://tomcat_pool;    #转向tomcat处理
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
}

换成

stream {
    
    
    upstream rtmp {
    
    
        server 127.0.0.1:8089; # 这里配置成要访问的地址
        server 127.0.0.2:1935;
        server 127.0.0.3:1935; #需要代理的端口,在这里我代理一一个RTMP模块的接口1935
    }
    server {
    
    
        listen 1935;  # 需要监听的端口
        proxy_timeout 20s;
        proxy_pass rtmp;
    }
}
  • 注意:需要重新编译nginx,需要加上:–with-stream,然后make & make install。
  • 勉强给一个解释:http应该需要会话保持,并且带上认证的token,而通过nginx的tcp转发到后端机器上,由后端机器去建立认证信息,nginx仅仅是转发tcp请求,不在应用层处理信息。
  • 排查思路:就是降低nginx的作用,仅仅做转发,那么由http层降级到tcp层。

现象2

  • 使用nginx做负载均衡的两台服务器其他服务均可以被访问,唯独某个服务不可以被访问,但是绕过nginx却可以单独访问。
  • 解决办法:
    • 试过上述方法后,发现时nginx转发的端口被占用了。占用的这个服务起的比较早,所以后续再起服务的时候,忘记了,这个端口叫做8888,大家千万不要使用这一类特殊端口。

猜你喜欢

转载自blog.csdn.net/ljfirst/article/details/108248724