记一次线上服务不可用问题分析原创
问题描述
线上接口服务隔一天就不能访问,重启大法好,只要重启就可以调用,间隔半天又会出现这种情况,由于该服务是mocker代理服务,所以开了443、80、11181等端口,但是只有80端口才会被阻塞,其他端口访问服务没有问题
问题排查分析
最初没有意识到和端口有关,所以下意识的去想是不是服务器宕机了,所以直接去环境敲命令,查看当前java PID
jps
获取当前web服务运行的pid之后,通过查看内存gc来判断服务器是否挂掉
jstat -gcutil ${pid} 2000 20
具体展示内容可以查看jvm相关命令学习,在这里简单说下这个gc是没有问题的,gc次数和时间没有明显异常,其实下面进行了一次ygc,截图没有截到,证明服务是没有问题的。
思考会不会是端口被安全组或防火墙给关了? 通过telnet 查看端口也是通的啊。
纳闷了,端口是通的,服务也没挂,到底是啥问题,要疯了,只能让运维保留客户现场,然后重启服务了。说到保留现场的话,建议设置jvm一个参数当OOM时,输出dump文件。
由于当前服务没挂,所以只能手动通过jmap命令来打包,具体去网上查一下,然后我又让运维把线程的日志文件给我down了一份,正是由于我多此一举的查看线程文件,才发现了问题的根源!!!!
不说了,先上图
jstack ${pid} > stack.log
想必各位大神应该差不多明白了,大量的线程处理80端口请求的服务,但是这些接口应该处理完了啊,逻辑也不是很复杂,为什么线程一直在运行呢~~。为了验证其他端口没有阻塞这个猜想,我用其他端口测一下接口服务,果然接口可以调通返回数据。
至于怎么去分析线程日志的话,说实话,还是要凭感觉的,因为线上大部分问题其实是由于锁操作或者其他某些问题导致线程死锁TIME_WAIT
或WAITING等,但是线程一直是RUNNABLE的出问题真的要靠猜了,我也是看到大量80线程一直运行才想到这方面。
既然项目里面没有运行完,说明进入80端口的服务没有处理完就被客户端关闭了,导致大量请求CLOSE_WAIT
,具体TCP/IP原理四次挥手原理建议小伙伴们去温习一下,这里就不多做描述了。
可以看到上图,大量请求没有完成就被动关闭了,至此,整个问题就分析完成。知道原因的话就好解决了。
解决问题
其实从分析打印出来的栈日志就能看到,是请求某个链接,没有返回导致接口长时间没有返回,但是后台请求其他服务的HttpClient
服务一般都是会设置SOCKET_TIMEOUT
和Connect_TimeOut
值,所以不应该会有这种问题出现的,为了验证这一问题,去看代码是最好的。
上图可以看到,左边是没有修改之前,并没有设置请求参数设置,右边是加了请求超时的配置,具体配置如下:
连接超时和读取超时时间都设置为5s,这样防止请求接口时,接口没有请求处理完发起关闭连接,导致问题重复发生。
至此,该问题就已经彻底解决了。
其实出现问题不可怕,可怕的是不知道哪里出现了问题。解决问题要按部就班,把所有可能出现的问题一一排查,走到最后总是能找到问题的根源所在,找到问题并解决问题,最后,希望你也可以顺利解决自己的问题。
本文作者:钟元