记一次网站没响应排查过程

Posted by Kaka Blog on August 25, 2019

现象描述

一天上班,同事跟我汇报给客户做的网站访问不了,我打开网站,响应很慢,几乎打不开,我登录到服务器,重启了服务,第一次访问还能进去,接着就几乎进不去,重复重启了好几次,就可以正常访问了,就没再去管它。

第二天,同事汇报网站又打不开了。我这次采用同样的方法,结果没有效果,在网上找了一些资料,修改了系统配置,结果又正常了。

到了第三天,同事又汇报,网站八点多的时候还可以访问,现在又访问不了了,我登录到服务器,这次没有头绪了。各种现象很奇怪,一时不知道问题在哪里。

问题排查

第一天,我进入到服务的日志,看到里面出现错误信息:java.io.IOException: Broken pipe,通过互联网发现这个应该是网络连接的问题,socket连接被中断了,导致服务访问不到,但也不知道在哪里出现问题,定位不到代码,之后重启服务没问题就没去理它。

第二天,重启服务后没有效果,我尝试看系统的tcp连接情况。

sudo netstat -anp

看到出现了很多TIME_WAITCLOSE_WAIT状态的连接

我在互联网搜索Linux tcp连接过多,CLOSE_WAIT过多,了解了TCP的三次握手,四次挥手过程:

tcp三次握手:
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认,SYN:同步序列编号
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

tcp四次挥手:
第一步,当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。
第二步,主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程 序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。
第三步,主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。
第四步,主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。

各个状态所代表的含义: 

     ESTABLISHED :代表一个打开的连接,数据可以传送给用户。     ##重点,可以查看同一时间,大约有多少个客户端连接你,可以查看并发量,一般大约几千个

     FIN-WAIT-1 :等待远程TCP的连接中断请求,或先前的连接中断请求的确认。
     FIN-WAIT-2 :从远程TCP等待连接中断请求。
     CLOSE-WAIT :等待从本地用户发来的连接中断请求。
     CLOSING :等待远程TCP对连接中断的确认。
     LAST-ACK :等待原来发向远程TCP的连接中断请求的确认。

     TIME-WAIT :等待足够的时间以确保远程TCP接收到连接中断请求的确认。  #指的是两个机器通信已完成,等待下次两个机器再次链接.

     CLOSED :没有任何连接状态。
  • 查看tcp连接状态统计信息
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

发现TIME_WAIT的数量很多,在网上找到一种解决方案,说可以通过调整内核参数解决,加速TIME_WAIT socket回收。

可以通过以下途径修改Linux配置文件

vim /etc/sysctl.conf
tcp_keepalive_time = 30
tcp_keepalive_probes = 3
tcp_keepalive_intvl = 5
net.ipv4.tcp_max_tw_buckets = 50000 表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。

使生效

sysctl -p

第三天,发现TIME_WAIT和CLOSE_WAIT的连接有两万多,吓到了,细想了一下,这个方法治标不治本,如果请求的速度大于回收的速度,还是是越积越多。看了下CLOSE_WAIT状态的连接,说是服务器没有关闭导致的,是服务端的代码有问题,但是在测试环境没有出现这个问题。而且试了回滚到之前的版本,也是一样的问题。CLOSE_WAIT状态都是本机地址的随机端口对本机的网关端口的连接,到这里知道了是因为tcp连接太多导致外部的连接进不来导致的,但是具体是哪里引起的还不清楚。

期间有想过会不会是被别人攻击了,但是看到里面有问题的连接都是本地IP,就没往那方面去想。所以我尝试找请求方端口对应的服务,但是没找到;重启了注册中心,同样也不行。有一个现象就是我把服务接口停掉,就能访问页面,只是没有数据展示,我原先还以为是网关服务的问题,一直很奇怪。其实我错了,数据接口服务也是通过网关去访问,之所以看到很多tcp是本地端口访问本机的网关端口,是因为要去访问数据接口服务,所以问题是出在数据接口服务。数据接口服务请求太多,响应不过来。

然后我去看了nginx的日志,nginx是做反向代理的作用,反向代理到网关端口。

tail -n 100 logs/access.log

发现里面有很多外地IP的访问,还有每秒钟都有再访问,这时我就想到是不是有爬虫在爬数据,我把服务关掉,仔细看里面的请求地址,都是直接请求后端数据接口的地址,这时我确定了是爬虫。终于找到的问题所在。

问题解决

找到了问题,就算是解决了一大半,只是反爬以前没做过,还得去了解怎么样去做反爬,最简单的可以通过nginx做反爬限制。

总结

一开始找错了方向,说明对系统运维还不熟,不过中间过程了解了一些技术,而且熟悉了部分底层技术。很多时候开发不困难,困难的是遇到问题怎么去分析问题,这就需要对综合能力要求很高,需要有系统的逻辑思维,去定位问题,找到原因,才能对症下药。通过这次考验,收获还不少,接下来就是爬虫的攻防问题了。最后要感谢互联网,不过网上很多都是抄来抄去,价值不大,还是要打好基础,遇到问题不慌不乱。