一、问题表现
最近对JAVA程序进行了例行的迭代升级,但部署时出现了一个诡异的情况:同一个JAVA程序,部署在当前环境中多台机器节点时。只有一台IP为25的机器在JAVA程序启动时抛出了“java.lang.NoSuchMethodError”的异常,无法启动,但其它机器均表现正常。
二、问题分析
首先核查了环境中所有主机的LINUX版本、JDK版本、JAVA程序版本,发现全部一致,没有差异。那么问题来了:
“java.lang.NoSuchMethodError”这个异常,是因为什么问题引起?为什么只有25机节点失败。JAVA不是一次编译,处处运行吗?
JAR包冲突
其实有经验的JAVA开发人员,如果发现同一个JAR包在不同的环境中表现不一,而失败的节点抛出的异常是“java.lang.NoSuchMethodError”、
“java.lang.ClssNotFoundException”(非笔误,完整关键字无法提交),“java.lang.NoClassDefFoundError,java.lang.LinkageError”时,应该会马上联想到,九成是jar包冲突了。不同的JAR包依赖有相同的类名,因为加载顺序未如预期。譬如本应加载A.JAR的MYCLASS,但结果加载到B.JAR的MYCLASS,最后的结果就是NoSuchMethodError,该依赖版本中的类中不存在该方法。
JAR包的加载顺序
当你的应用程序在CLASSPATH中需要加载大量的JAR包时,JAR包的加载顺序是和指定的顺序相关的。譬如 java -cp a.jar:b.jar,那么a.jar就会先于b.jar加载。那么当前程序是如何指定CLASSPATH的加载JAR包的呢?结果如下:
从上面的脚本可以看到,是以指定目录下的JAR文件来加载的?那马上就会迎来第二个问题
为什么25机的加载JAR包的顺序和其它机器不一样?查看CLASSPATH所在磁盘的文件系统:
25机:
其它机器:
只有25机的默认数据目录/data是EXT4文件系统,而其它机器均是XFS文件系统。
文件系统的文件加载顺序,依赖于底层文件系统返回的顺序,当不同环境之间的文件系统不一致时,就会出现有的环境没问题,有的环境出现冲突的情况。Ext4文件顺序与目录文件的大小是否超过一个磁盘块和文件系统计算的Hash值有关。
三、问题的解决方案
- 临时方案,当前从日志中可以得知是NETTY类方法报错,优先加载NETTY的相关JAR包,问题解决。
- 最终方案,在CLASSPATH中指定JAR的加载顺序。