一、背景
项目新拉了一个分支开发,前几天好好的,加了一些功能后发现部署一直失败。
总是报同一个错误(详见2.1),而且并不像是自己代码的错误。
和同事花费了一些时间进行排查,最终定位并解决问题,记下分析思路。
二、排查步骤
注:截图均来自自己的本地练习项目,而不是排查一模一样的截图,只是演示效果。
2.1报错内容
代码语言:javascript复制java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:941)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:872)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1421)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1411)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
... 6 common frames omitted
Caused by: org.apache.catalina.LifecycleException: Failed to start component [Pipeline[StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5166)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 6 common frames omitted
Caused by: org.apache.catalina.LifecycleException: Failed to start component [org.apache.catalina.authenticator.NonLoginAuthenticator[]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
at org.apache.catalina.core.StandardPipeline.startInternal(StandardPipeline.java:182)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 8 common frames omitted
Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String;
at org.apache.catalina.authenticator.AuthenticatorBase.startInternal(AuthenticatorBase.java:1186)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 10 common frames omitted
2019-05-28 11:30:28.605 WARN 25 --- [main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat
2019-05-28 11:30:28.640 ERROR 25 --- [main] o.s.boot.SpringApplication : Application startup failed
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:137)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
2.2 尝试
第一反应是给出的错误信息非常不明确,就是说tomcat启动失败。
(1) 由于这个项目是一个maven项目,直接用maven编译命令查看是否可以编译成功,发现都SUCCESS.
(2) 尝试把代码发布到不同的环境,查看是否是环境造成的,发布到了其他环境上,偶尔能成功,发布master都成功,排除了环境bug。
(3) 在google上搜报错信息,大多数提示是servlet-api的jar包冲突。
然后使用mvn dependency:tree 查看依赖树,发现新引入的几个jar包其中有一部分确实包含了servlet-api。
然修改pom.xml文件,排除servelt-api相关依赖。
发现依旧报错。
(4) 由于新增了多个jar包不确定是哪个jar包引起的,因此基于这个分支新拉出一个分支用于删除依赖排错。
删除最可以的新的jar包以及相关的本地代码,重新部署,发布成功。
然后定位到此jar包的servlet-api是通过jboss的包引入的。
由于这是第二方的dubbo的调用api jar包,因此可以放心去掉jboss。
直接排除了jboss的依赖。
三、总结
3.1 擅于使用IDEA插件
项目右侧的maven选项卡里 有”show dependencies“
可以查看依赖,可以搜索,可以放大等
可以查看冲突,保存UML等
另外还可以安装 Maven Helper插件
方便的搜索并排除依赖。
安装后pom.xml文件会多一个”依赖分析“选项卡
切换过去会显示冲突的jar包,也可以切换到All Dependencies 列表或者树,也可以在上搜索自己想看的jar包。
查看依赖,查看冲突,并可以方便的通过右键”exclude“自动在pom.xml文件中的该依赖里排除该jar包。
3.2 要积累并实践排错方法
3.2.1 细心
要细心,刚开始以为提示信息不多,其实并没有注意到
Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String;
这行关键的错误,很明显极有可能是servlet的jar冲突。
3.2.2 要积累和实践
上面是总结的排查问题的方法,在这次排查中就用到了几个关键的部分。
1 code review 和master比代码,分析新增的依赖
2 日志
3 命令
(1)mvn命令,包括查看依赖树,编译查看是否通过等
(2)git命令,拉取新分支排查问题,避免之前的分支代码错乱污染、误删等
3 换环境
4 搜索引擎
5 控制变量法
3.2.3 知其所以然
(1)为啥dubbo的接口包会引入Jboss呢?
猜测其参数或者返回值里有jboss相关的类,进源码搜索,发现果然如此。
(2)为啥排错就行了呢?冲突怎么自动决定用哪个jar呢?
要顺便了解一下maven的依赖传递机制,参见https://www.cnblogs.com/ygj0930/p/6628429.html
1:如果依赖路径的长度不同,则“短路优先”:
A—>B—>C—>D—>E—>X(version 0.0.1)
A—>F—>X(version 0.0.2)
则A依赖于X(version 0.0.2)。
2:依赖路径长度相同情况下,则“先声明优先”:
A—>E—>X(version 0.0.1)
A—>F—>X(version 0.0.2)
则在项目A的中,E、F哪个在先则A依赖哪条路径的X。
如果想排查传递依赖,以用 标签。
四、总结
快速定位和解决问题是一种能力,需要积累和实践。
很多人遇到问题,尤其是新手,不是先思考,而是先百度,先问别人。
更可怕的是,没有这种积累方法的意识,目光短浅,只着眼于解决眼前的问题,剩下的”以后再说“。
总是有很大碰运气的成分,也容易浪费很多时间,自己也无法提高排错的能力。
网上有为了解决这个问题mvn装了好几次,搞了一两天都不行的,其实就是没有思考到问题的核心。
希望大家可以积累排错能力,而不是毫无头绪地,不断试错。
另外我们经常使用idea却可能并没有安装高效的插件,并不熟悉其高效的快捷键。
我们经常使用Maven,但是并没有系统掌握mvn所有原理和用法。
我们经常使用spring/mybatis却很少有人系统看过期官方文档,更别说读懂源码。
我们经常。。。
我们加入了很多技术群,真正分享技术、真正帮助解决问题的的人很少。
真正有人分享干货自己却未必感兴趣,未必去下载去看,总是遇到问题了才想到着急。
其实排错,看似是技巧,其实和基础和经验无不相关,我们工作的同时要多思考为什么,积极学习核心技术的的源码。
本文也分享了自己总结的排查思路,大家如果有其他好的思路也欢迎补充,共同进步。
五、其他
之前写过一篇排错的方法总结《代码排错和避免错误的正确姿势》感兴趣可以参考一下
https://cloud.tencent.com/developer/article/1868978
如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。 另外欢迎加入我的知识星球,知识星球ID:15165241 一起交流学习。 https://t.zsxq.com/Z3bAiea 申请时标注来自CSDN。