360SDN.COM

首页/Nginx/列表

解决Nginx+Tomcat出现间隙性502问题

来源:小鲁班 乐得SRE   2018-08-22 16:13:56    评论:0点击:

Nginx+Tomcat是网站常用的一种架构,Nginx作为反向代理和7层负载均衡器,Tomcat负责具体业务处理,也可以让Nginx处理静态页面的请求、Tomcat处理JSP页面请求达到动静分离的目的,二者结合堪称完美,关于两者如何结合使用,网上已经有很多介绍,本文就自己亲身经历的Nginx诡异返回502问题的解决过程作以介绍,并着重描述如何进行Trouble Shooting。
问题描述

    如上拓扑图显示,我们的服务访问流程是这样的:前端Nodejs服务发出的http请求需要通过同网段的LVS和Nginx转发到后端Tomcat服务;前端服务和后端服务一起上线的,上线后第一天前端日志里就出现了一条502访问错误的报警,是从两台Nginx的其中一个返回的502错误,每天单台Nginx由前端node发来的请求的日访问量平均维持在12000,之后观察了一周,几乎每天会有一两条502错误报警。

 
Throuble shooting
 
 
 
 

第一步:踩坑

 
 
 
 
 

     由于502一般是由于后端服务不可用或者Nignx到后端的通信问题导致,先检查Tomcat应用日志有无报错,Nginx到tomcat服务端口的通信情况,结果发现发生502的这条请求根本没有到达应用程序,而且由于没什么访问量,"ps -eLf|grep java " 查看Tomcat线程数也很少,查看cpu、内存等系统性能也很正常,通过Zabbix监控也确认丢包率为0,网络通信并无问题,而且Nginx和Tomcat都部署在同一网段,通信不可能有问题,这就奇怪了?难道是Nginx配置的问题?

       于是求助万能的谷歌、百度,搜“nginx 502”关键字会出现大量php fast-cgi相关的502问题,Tomcat的比较少,而且多数为并发量大,负载高,线程数不够等性能方面引起的持续性报502错误的情况,完全跟自己的问题现象不一致,算了只能自己动手,丰衣足食,从根本底层上寻找问题原因。

    查看nignx错误日志/etc/nginx/logs/error_xxxx.log,只有两种报错,每天大概40条左右,而且这两种错误会引发"no live upstreams while connecting to upstream",即nignx 502:

 
 
 
 
 
 
 
 

第二步:tcpdump抓包
    为了快速复现502错误,必须对该业务进行压测,因为是线上机器,跟测试沟通过
后,由于这个业务比较鸡肋,没什么人访问,于是对该业务以每秒100并发数进行压测,间隔时间记不清了,同时用tcpdump工具进行分别在两台Nginx机器上抓取到两个后端的数据包:

 

10.200.80.83: tcpdump -i eth0 host 10.200.80.83 and  host 10.200.80.41 -w /data/tcpdump_83_41

10.200.80.83: tcpdump -i eth0 host 10.200.80.83 and  host 10.200.80.42 -w /data/tcpdump_83_42

10.200.80.84: tcpdump -i eth0 host 10.200.80.84 and  host 10.200.80.41 -w /datat/tcpdump_84_41

10.200.80.84: tcpdump -i eth0 host 10.200.80.84 and  host 10.200.80.42 -w /datat/tcpdump_84_42

      经过两小时多,终于出现502错误,停止抓包。

      用Wireshark数据包分析工具分析抓取的数据包,下载抓取的数据包到本地(我的是windows系统),用Wireshark打开,发现以下异常数据包,而且时间节点和Nginx error日志报502错误的时间节点一样,那肯定就是问题所在。

 
 
 
 
 
 
 
 
 

 


第三步:现象

      如上第二步中两张图,由于本次压测,502错误是在10.200.80.84的Nginx上发生的,在此只展示了10.200.80.84的Nignx到两台后端Tomcat的数据包,下面对上图进行详细分析:

 1   10.200.80.84 到10.200.80.41发送SYN并通过三次握手建立TCP连接

 2   10.200.80.84 到10.200.80.41发送http请求数据,10.200.80.41把响应数据返回给10.200.80.84的Nginx,Nginx再返回给后端ACK确认

 3  直到最后一个ACK确认了后端响应数据,如图,即seq=775,ACK=1216的ACK发送给后端tomcat

 4   经过60s的空闲时间,由于后端tomcat配置connectiontimeout=60s,即tcp连接有超过60s空闲,Tomcat会强制断开TCP连接

 5   所以经过60s后,在11:14:27时间点时,后端需要发起FIN断开TCP连接,但从图中看到,在TCP四次挥手过程中10.200.80.84的Nginx端正好利用该TCP连接发送GET请求数据(seq=775,ACK=1216),后端由于已经发送了FIN(seq=1216,ACK=775)关闭连接,此时TCP连接处于半连接状态,根据TCP协议约定,对于该数据直接回复RST(seq=1216)

 6   Nginx端收到RST,并继续TCP挥手,发送给Tomcat端FIN(seq=1037.ACK=1217)来彻底断开该连接

 
 
 
 
 
 
 
 
 

 

 
 
 
 

第四步:分析

 
 
 
 

 

 
 
 
 
 
 
 

       这是一个很巧合的问题,由于后端Tomcat 的connectiontimeout参数设置为60s,即如果从最后一次请求开始计时,计时到60s时,Nginx还没有利用该TCP发送数据,那么Tomcat会调用close()这个sockect函数来强制关闭连接的两个传输方向,由于Nginx端对Tomcat是否断连接是无感知的,此时恰好Nginx端发送了一条Get数据,而Tomcat端已经调用close()函数关闭了连接的两个传输方向,因此不会接受这个数据,直接返回RST,那么这个请求还没发送到Tomcat应用程序就被Reset了,这次请求就失败了,随后,10.200.80.84的Nginx按照设定的upstream策略把下一个请求发送到10.200.80.42的Tomcat,从下面的图可以看出,也恰巧发生了同样的问题,因此10.200.80.84这个Nginx判断两台后端机器都不可用,因此报出502错误。

 
 
 
 
 
 
 
 

 

 

第五步:原理

 
 
 
 

       在理解Tomcat怎么关闭连接之前,先看看正常的TCP协议的半关闭状态是怎么回事?

       从下图可以看到,当主动关闭端发送FIN并收到对端的ACK,说明主动关闭段已经不能再发送数据了,但是没有收到对端的FIN之前,还可以接收收据,或者接收完未传输完的数据,然后回复ACK确认数据已经收到,最后对端发送FIN关闭另一半连接。

       半关闭状态的实现API是伯克利套接字函数shutdown函数,当应用程序希望这样关闭连接时,即“我已经完成了数据的发送工作,并发送了FIN给对方,但是我仍然能接收对方的数据直到对方发送FIN给我”,就可以使用shutdown()函数来实现。

        然而,大多数应用程序使用了close函数,而不是shutdown函数,这里就要明白shutdown和close的区别:

 

 

close函数:

 1   在多进程的情况下,关闭本进程的socket id,但链接还是开着的,用这个socket id的其它进程还能用这个链接,能读或写这个socket id,直到所有的进程都进行了close,才真正关闭这个套接字

 2  它真正执行关闭的时候是完全关闭,既不处理发送也不处理接收数据,如果对端发送数据过来会收到RST消息

 

 

shutdown函数

 1  即便在多进程的情况下面,也是直接进行关闭的,破坏了socket 链接,其他进程的链接也会被关闭

 2  但他关闭的时候只关闭一半,即发送数据通道关闭,接收数据还是可以的,如果写则可能会收到一个SIGPIPE信号,这个信号可能直到socket buffer被填充了才收到

 

        而Tomcat是用close函数来关闭连接的,即完全关闭,关闭后对端发送数据或数据还在内核Buffer队列,均不做处理,直接回复RST消息,说到这里就能明白发送到半连接状态的数据为什么被Reset了。

 
 
 
 
 
 

 

 
 
 
 

第六步:解决办法

 
 
 

 

 
 
 
 
 
 
 

       从上数分析可以看出,要发生这一现象,必须满足两个条件:

    1  恰好有相隔时间为60s的GET请求利用同一个TCP连接传输数据

    2  后端使用close函数断开连接的超时设置也为60s

      因此,目前是改变后端Tomcat的connectiontimeout参数来设置,具体设置为多少,根据业务而定,只要使两个频率不要保持一直,比如我们的业务请求间隔大概在10s,20s,30s,60s,90s居多,我们第一次调整为20s,虽然Nginx日志错误大大减少,但一周还是会有一两次发生502错误的概率,于是我们又调整为23秒,这样极少会发生上述现象,一条Nginx最多有两条错误,大多数不报错,并且一直没再引发502错误,这也印证了我们找出的原因确实是问题所在。

 
 
 
 
 
 
 
 

 

 
 

经验总结

1.根据自己的工作经验处理。

2.上网搜索别人是否有同样经验,先解决问题,再寻求原理。

3.根据原理及数据分析问题,一般的数据就是网络数据包和日志。

为您推荐

友情链接 |九搜汽车网 |手机ok生活信息网|ok生活信息网|ok微生活
 Powered by www.360SDN.COM   京ICP备11022651号-4 © 2012-2016 版权