今天同事告诉我,生产环境的kafka队列中没有数据了。生产环境中和数据生产者相关的服务器有2台,最近这两台老搞事情:
客户端是基于xsocket开发的,服务端是直接使用NIO开发的。
背景交代完了,下面回顾一下我排查这个问题的思路 :
查日志
半天查不到什么有用信息,遂放弃。
检查socket是否还在
看日志之前就怀疑socket是不是断开了。这算是查这类问题的套路吧。
在A机上运行netstat,命令就不细说了:netstat -ntap | grep xxx #xxx是B机监听的端口号
还好socket还在。
然而,现在仍然判断不来AB机之间是否有问题,还得想办法。检查网络流量是否正常
我想,既然socket还在,那就看看socket上有没有流量。流量正常的话,基本可以排除客户端和服务器之间的问题。
使用iptraf命令查看socket流量,在A机上运行iptraf:
此处应有截图,然而我忘截了
iptraf的结果是,socket上面有数据流,但是远少于正常情况,隔一阵一小包数据。这说明问题出在A、B机之间。我猜想小包数据是登录报文或者心跳包一类的。
抓包看报文
为了验证刚才的猜想,我决定抓几个包来瞧瞧。在A机上运行下面命令:
tcpdump tcp port xxxx and host xxx.xxx.xxx.xxx
抓了好长时间,报文都是上面那样的,不是我猜的登录或心跳报文(报文数据长度为0)。我抓到的大概是tcp协议的心跳一类的吧。
当时分析,A、B之间没数据,大概会是这几种情况吧:- A机发数据线程没创建起来/已经终止
- A或B机阻塞在某处
- 特殊情况下代码逻辑跳过了发数据部分
- A机没有要发送的数据
仔细检查了代码逻辑,排除了后两种情况。
使用jstack查看线程堆栈
从socket上已经找不出更多线索了,只好去看jvm。在A机上连续使用jstack查看线程状态几次,发现发数据的那个线程的堆栈状态就没变过,仔细一看,线程发生了死锁!
找到问题的原因,还有了详细的堆栈调用层级,接下来就好办了。找到xsocket的源代码,找到死锁的那一行,发现那行下面就有一个警告日志:
synchronized flushing in NonThreaded mode could cause dead locks (hint: set flush mode to ASYNC).
在程序中设置flush mode为异步,问题得到解决~