记spring-boot项目启动卡住问题排查记录

问题背景

一个spring boot开发的项目,spring boot版本是1.5.7,携带的spring版本是4.1.3。开发反馈,忽然在本地启动不起来了,表象特征就是在本地IDEA上运行时,进程卡住也不退出,应用启动时加载相关组件的日志也不输出。症状以下图:java

问题分析

由于没有有用的日志信息,因此不能从日志这个层面上排查问题。可是像这种没有输出日志的话,通常状况下,确定是程序内部启动流程卡在什么地方了,只能经过打印下当前线程堆栈信息了解下。通常状况下,在服务器环境,咱们会使用java工具包中的jstack 工具来查看:如jstack pid(应用java进程)。git

可是,在IDEA本地开发的话,IDEA内置了一个工具,能够直接查看当前应用的线程上线文信息,如:spring

注意下面那个箭头指向的像照相机同样的图标,故图思意,就是打印当前线程快照的的意思。点击后,就出现了右边那些线程上下文信息了,能够看到有不少的线程,咱们主要关注下main线程,线程状态确实是waiting的,接着点击箭头所指向的main线程,能够看到以下内容:bootstrap

"main@1" prio=5 tid=0x1 nid=NA waiting java.lang.Thread.State: WAITING at sun.misc.Unsafe.park(Unsafe.java:-1) at java.util.concurrent.locks.LockSupport.park(www.ztyLegw.cn  LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304) at java.util.concurrent.CountDownLatch.await(www.wanhaoptdL.com CountDownLatch.java:231) at org.springframework.boot.autoconfigure.BackgroundPreinitializer.onApplicationEvent(BackgroundPreinitializer.java:63) at org.springframework.boot.autoconfigure.BackgroundPreinitializer.onApplicationEvent(BackgroundPreinitializer.java:45) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:158) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) at org.springframework.boot.context.event.EventPublishingRunListener.finished(EventPublishingRunListener.java:115) at org.springframework.boot.SpringApplicationRunListeners.www.tcgjgw.com callFinishedListener(SpringApplicationRunListeners.java:79) at org.springframework.boot.SpringApplicationRunListeners.finished(SpringApplicationRunListeners.java:72) at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:745) at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134) - locked <0xea6> (a java.util.concurrent.atomic.AtomicBoolean) at org.springframework.cloud.bootstrap.BootstrapApplicationListener.www.chaoyuL.com bootstrapServiceContext(BootstrapApplicationListener.java:175) at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:98) at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:64) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74) at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54) at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:325) at org.springframework.boot.SpringApplication.run(SpringApplication.java:296) at cn.keking.project.customerManagement.KekingCustomerManagement.main(KekingCustomerManagement.java:36)

能够看到是经过CountDownLatch.await()阻塞了线程,接着看下面那行,所在代码块以下:安全

private static final CountDownLatch preinitializationComplete = new CountDownLatch(1); @Override public void onApplicationEvent(SpringApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { if (preinitializationStarted.compareAndSet(false, true)) { performPreinitialization(); } } if (event instanceof ApplicationReadyEvent |www.chengmyuLegw.cn| event instanceof ApplicationFailedEvent) { try { preinitializationComplete.await(); } catch (InterruptedException ex) { Thread.currentThread(www.cmylwg.com/).interrupt(); } } }

这是spring boot中的一个安全初始化资源的一个类,代码所示为监听SpringApplicationEvent事件,能够确定的是,它的逻辑执行到了 preinitializationComplete.await();这里,致使了线程阻塞了。正常状况下,spring会触发ApplicationEnvironmentPreparedEvent事件完成资源的初始化,这里先不深究Spring为何这么作,主要经过程序逻辑看下为何卡这里了,在preinitializationComplete.await();所在行打个断点,看下event对象里的信息,以下:服务器

原来event是一个Spring上下文初始化失败的异常事件对象,对象里包含了具体的异常信息,如箭头所指,关键异常信息如:ide

NoSuchMethodError:"org.springframework.util.ObjectUtils.unwrapOptional(Ljava/lang/Object;)Ljava/lang/Object;"工具

假设问题

经过上面的分析,基本定位到Spring boot应用启动卡住这个表象背后的真实缘由了,并且也定位到了异常信息。ui

出现NoSuchMethodError异常,是由于调用方法的时候,找不到方法了。通常出如今两个有关联的jar包,可是版本对不上了,也就是常说的jar版本依赖冲突。查看了下,ObjectUtils是spring-core包里的一个类,当前的4.1.3版本确实没有这个unwrapOptional方法,spring-core-5.x的版本才新增了这个方法。由于以前的依赖是没有问题,为何如今spring上下文会调用5.x的版本的方法呢?搜索引擎

因此先假设近期有开发在pom.xml里添加了新的的依赖,致使了这个问题。

当心求证

有了找问题的方向就好办了,由于代码都是git管理维护的,因此查看下pom.xml文件近期的提交记录便可,查看后,确实发现了近期对pom.xml有改动,添加了一个依赖:

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE<www.yuntianyuL.cn /version> </dependency>

这里还涉及到一点Maven依赖优先级的问题,在pom.xml里直接这样添加的依赖优先于其余jar中pom.xml依赖的,也就是说,即便spring boot1.5.7自带了Spring-context.4.1.3,可是这样指定后,应用最后依赖的仍是5.1.6的版本。具体的Maven依赖关系,能够参考个人博文《关于Maven的使用,这些你都了解了么?》。结合以前的分析,八九不离十了就是由于加了这个依赖致使的问题,spring-context.5.1.6配合spring-core.4.1.3确定得出问题啊。直接移除这个依赖,而后启动系统一切正常,日志打印了Spring加载上线文的信息。

问题总结

定位这个问题的关键在于要了解java中线程堆栈的知识,在没有足够异常日志状况下经过线程快照排查问题。在定位到问题后,如NoSuchMethodError这样的异常,须要平时的经验积累来假设问题的真实缘由,而后在追本溯源验明问题所在根本缘由。找问题本质必定要这种按部就班的思路。举例,出现这种问题,若是你直接去搜索引擎搜:“Spring boot应用启动卡住了”,是搜不出来什么东西的,可是当你发现了是因为jar冲突。去搜索引擎搜索:

“NoSuchMethodError:"org.springframework.util.ObjectUtils.unwrapOptional(Ljava/lang/Object;)Ljava/lang/Object;"”。就会有不少的内容,很容易解决问题。

相关文章
相关标签/搜索