排查由于编译版本过高导致的部署失败的问题

事件简介

技术背景简介

类Dubbo分布式系统,本人职责网关,通过Maven包调用多个后端通用平台接口,以将不同平台的数据进行整理融合,并向前端提供基于需求的偏垂直接口。

事件描述

进行技术修改后准备上预发,通过基于jackens的集成平台进行编译后部署失败,报错如下:

18:34:15.374 [localhost-startStop-1] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [context/spring-config-context.xml]; nested exception is java.lang.UnsupportedClassVersionError: javax/annotation/ManagedBean : Unsupported major.minor version 52.0 (unable to load class javax.annotation.ManagedBean)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:412)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:131)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:522)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:436)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:385)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:284)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5016)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5528)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:672)
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1882)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.UnsupportedClassVersionError: javax/annotation/ManagedBean : Unsupported major.minor version 52.0 (unable to load class javax.annotation.ManagedBean)
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2961)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1210)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1690)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.registerDefaultFilters(ClassPathScanningCandidateComponentProvider.java:201)
at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.<init>(ClassPathScanningCandidateComponentProvider.java:104)
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.<init>(ClassPathBeanDefinitionScanner.java:138)
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.<init>(ClassPathBeanDefinitionScanner.java:112)
at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.createScanner(ComponentScanBeanDefinitionParser.java:129)
at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.configureScanner(ComponentScanBeanDefinitionParser.java:99)
at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(ComponentScanBeanDefinitionParser.java:83)
at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:73)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1419)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1409)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:184)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:140)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:111)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
... 26 common frames omitted

问题排查与探索

错误日志解析

经搜索,此问题出现的原因是.java文件使用高版本jdk进行编译,然后部署在低版本jre上。导致Java虚拟机加载.class文件时无法识别开头的版本编号,进而导致部署失败。

问题关键字:高版本编译、低版本部署.class版本编号

我遇到的问题,在日志中表示的很明确:

如果是纯粹的Spring自定义标签解析,且完全在xml中完成所有定义。在解析xml并生成BeanDefinition时没有问题,这个很好理解,因为我们在存储Bean定义时仅将类名以字符串形式存储,不进行类加载。

但是在这里,根据调用堆栈,我们调用了parseCustomElement(),及定制标签的解析,在定制标签的解析中,我们发现有类似ConpoentScan的字样,虽然我们没有把Spring原理完全读完,但是根据我们对Spring 框架的了解和日常使用的经验,可以大概描绘出车祸现场:

  1. 我们依赖的后端提供的maven版本中存在高版本jdk编译的class
  2. 我们在进行xml标签的解析时,通过引入其他基于Spring的框架,启动了对Java代码的扫描,类似ComponentScan,以达到通过XML进行基本信息配置、通过JAVA注解简化业务相关配置,以提高编码效率,在加载要扫描项目中的类时,接触到了高版本的class,jvm无法完成类加载操作,故报错。

另外,我们的报错是52,Java版本和编号的对应关系如下:

  • Java 1.2 uses major version 46
  • Java 1.3 uses major version 47
  • Java 1.4 uses major version 48
  • Java 5 uses major version 49
  • Java 6 uses major version 50
  • Java 7 uses major version 51
  • Java 8 uses major version 52
  • Java 9 uses major version 53

因为项目太过老旧且代码极其混乱,目前此项目的运行环境还是Java7。

综合以上描述,问题陈述如下:

我们使用Java8编译,使用Java7部署,导致部署失败。

解决方法:

  1. 提升部署的Java版本——不可行,此项目存在大量的旧框架,提升Java版本意味着大量的包升级
  2. 降低编译版本——可行

定位问题所在

首先,检测jackens配置,经检测,编译配置正常,是使用jdk1.7进行的编译。猜测是后端背着我们偷偷发包升级编译版本了

检测maven依赖的Java版本号

依赖版本检测

从jackens上把打好的war包下下来,100M。。。。。其实有用的不多,实在没时间清理。。。从网上搜索的到反编译获得Java版本的命令:

javap -verbose MyClass | grep "major"

但是我们从war包中找到的后端依赖都是jar,需要进行解压,上网查询的到结果如下:

jar xf ~/a/b/c.jar

此命令可以进行解压,但是会将解压结果输出到当前目录,且不可定制

unzip ~/a/b/c.jar -d ~/d/e/f

此命令可以在解压是进行目录控制,但是在某些jar包解压时,可能存在文件覆盖问题,不知道原因,可能是后端打的包有点烂吧,我们后续在探究。

考虑优劣情况,我们采用第一条和第二条命令进行判断,通过在shell切换目录实现解压后文件的隔离

因为我们项目依赖的包太多,故采用shell脚本进行判断:

for file in `ls ~/Desktop/xxx-20190626182927/WEB-INF/lib`;do
    echo "=================================开始检测包${file}==========================="
    mkdir ~/Desktop/target/temp/${file}
    echo "完成临时目录创建"
    cp ~/Desktop/xxx-20190626182927/WEB-INF/lib/${file} ~/Desktop/target/temp/${file}/${file}

    cd ~/Desktop/target/temp/${file}
    jar xf ~/Desktop/target/temp/${file}/${file}
    echo "完成解压,抽取一个类,生成此类的Java版本为"

    javap -verbose $(find ~/Desktop/target/temp/${file} -name *.class | head -1) | grep "major" 
    
done

输出情况如下:

1.png

因为打印的东西实在有点多,13页,我们只截一部分的图。

检测高版本jar包引入路径

进入项目目录,执行如下命令:

mvn dependency:tree

得到maven依赖树,涉及公司内部代码,就不粘了。确定两个高版本maven包。

敲定解决方案

打卡下班,明天开怼!!!!

反思

  1. 在以后我们作为Dubbo中的服务提供者角色时,提供包时要考虑到版本兼容性,可以考虑依据groupid进行多jdk编译版本的maven包维护
  2. 在进行版本升级时,尽量邮件或者当面通知所有调用方,避免为他人带来不便
  3. 好好学学shell命令,这次一半多时间是花在写shell上

参考文献

https://blog.csdn.net/sweettool/article/details/77203193

https://www.runoob.com/linux/linux-shell-variable.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容