前言
最近在某应用更新代码后部分机器发布失败,发布失败的机器上Tomcat一直没有启动成功,日志卡在Deploying web application,重启数次之后仍然是一样的情况。所以进行排查问题,下面记录了所有的排查过程,需要的朋友们可以参考学习。
排查过程
1. Tomcat启动线程卡住
下文中Tomcat启动线程代指线程名为localhost-startStop-$id的线程。
使用jstack打印出Tomcat的线程堆栈:
jstack `jps |grep Bootstrap |awk '{print $1}'` > jstack.log
从jstack.log里面可以看到线程localhost-startStop-1处于WAITING状态,堆栈如下:
"localhost-startStop-1" #26 daemon prio=5 os_prio=0 tid=0x00007fe6c8002000 nid=0x3dc1 waiting on condition [0x00007fe719c1e000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007147be150> (a xxx.heartbeat.network.client.FutureResult) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429) at java.util.concurrent.FutureTask.get(FutureTask.java:191) at xxx.HeartBeatContainer.invoke(HeartBeatContainer.java:183) at xxx.HeartBeatContainer.registry(HeartBeatContainer.java:130)
对应的代码如下:
final ResponseFuture<XxxMessage<Result>> future = responseFutureFactory.newResponseFuture(request); channel.writeAndFlush(request); XxxMessage<Result> response = future.get();
线程一直卡在future.get()没有返回。这个步骤是在等待客户端向Xxx-Server发送的注册请求的返回。
2. Xxx注册请求没返回
用tcpdump抓了下包(Xxx-Server的服务端口是yyy):
tcpdump -X -s0 -i bond0 port yyy
发现只有建连接的包,没有length != 0的数据包:
IP app-ip.56599 > xxx-server-ip.yyy: Flags [S], seq 3536490816, win 14600, options [mss 1460,sackOK,TS val 3049061547 ecr 0], length 0 IP xxx-server-ip.yyy > app-ip.56599: Flags [S.], seq 2500877640, ack 3536490817, win 14480, options [mss 1460,sackOK,TS val 1580197458 ecr 3049061547], length 0 IP app-ip.56599 > xxx-server-ip.yyy: Flags [.], ack 1, win 14600, options [nop,nop,TS val 3049061548 ecr 1580197458], length 0
所以,推断注册请求没返回的原因是请求压根儿没有发送出去。
3. Xxx注册请求没发送出去
Xxx代码里面调用了channel.writeAndFlush,但是数据却没有发送出去。这块的代码,更友好的做法应该是writeAndFlush之后对返回的ChannelFuture注册一个Listener,在write操作完成之后的回调里面判断状态。
在Netty大神 – @yh的指导下用BTrace跟了一下Netty的代码。
在Tomcat启动逻辑相关脚本bin/catalina.sh里面加上参数让Btrace agent和Tomcat一起启动:









