起因
服务B上日志显示,请求服务A连接被拒(Connection refused)???
- 猜测一 服务进程崩了?
登录时服务A的机器,
ps -ef|grpep java
查看jvm还在。
- 猜测二 端口绑定错了?
服务端口通过配置参数-Dserver.port=8080绑定,
ps -ef|grpep java
还是通过这个命令查看,端口是8080没错
telnet 127.0.0.1 8080
响应Connection refused
netstat -lntp|grep 8080
没有结果。 jvm进程启动成功,端口没绑上去???
- 猜测三 服务hang住了或死锁
top -Hp
pgrep java
查看jvm进程的线程详情,看那个cpu使用率最高,cpu使用并不多,而且cpu使用率也是来回在换,不像死循环。
printf "%x\n" {pid} 找了个相对比较活跃的进程号,格式转为十六进制,准备到打印线程快照找一下是哪个线程有问题。
jstack -lpgrep java
> stack.log查看线程,搜索{十六进制},看到是vert.x-eventpool-thread-0线程,状态是RUNNABLE稳稳的。搜索blocked/dead关键字没发现问题。
- 猜测四 看一下log文件
最后一段日志是,服务重启了,启动过程中在调用DB相关Bean foo时报org.springframework.beans.factory.UnsatisfiedDependencyException,原因是foo在注册时因为“Too many connections”导致数据库连接不成功,时foo注册失败,spring boot报错,且应用启动失败
Application startup failed
。
这么看来问题已经找到,spring容易启动失败,所以端口也没去绑定。但是spring失败了,为什么jvm没结束???
其实在前面的jstack日志里可以发现原因
grep 'nid=' stack.log|grep -v 'daemon' 查看非守护线程数,还有好几个线程还没结束。
来回顾下jvm结束的条件:所有非守护进程结束时,jvm自动退出
。
问题总结
问题原因是spring boot在启动因为Bean注册失败,抛异常而启动失败,但由于有其他非守护线程存在,而jvm不能正常结束。
解决方案
通过spring的FailureAnalyzer捕获异常UnsatisfiedDependencyException,把未关闭的线程资源关闭掉,做统一的处理,jvm就能正常关闭。
FailureAnalyzer使用
public class DBFailureAnlyzer extends AbstractFailureAnalyzer<XXXException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, XXXException cause) {
//do something
return new FailureAnalysis("由于xx原因,应用启动失败", "清理存活的线程", cause);
}
}
在META-INF/spring.factories文件中添加一下内容
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.xx.www.DBFailureAnlyzer,\
com.xx.www.DBFailureAnlyzer1