问题现象
某天,某个应用搞新的迭代,突然报ElasticSearch 7.17.5 相关操作都失败了,且问题是必现,本地启动也能稳定复现。组内小伙伴按照es jar包冲突排查了一番,无果,于是问题转交给我来排查。
错误信息是:
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.elasticsearch.client.RestHighLevelClient
at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClient.restHighLevelClient(ElasticSearchRestClient.java:128)
at cn.tongdun.yuntu.common.util.es.restclient.ElasticSearchRestClient.<init>(ElasticSearchRestClient.java:78)
at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClientFactory.getEsRestClient(ElasticSearchRestClientFactory.java:65)
at cn.tss.yun.tech.component.biz.service.impl.ElasticSearchService.getElasticSearchRestClient(ElasticSearchService.java:48)
排查分析
刚开始看到错误信息,注意,此处是NoClassDefFoundError,表明类是存在的,但是初始化时出错。如果是真的class文件都找不到,应该是报ClassNotFoundException.
我也按照ES jar包冲突的方向排查。一通解压反编译,排查下来,ES的相关的类和包,都没有版本冲突问题,和代码中使用的包路径,构造方法等,都是吻合的,但这里怪异的是,在其他应用中ES是正常使用的。
经过两次启动调试,笔者发现一个规律。应用在启动时,第一次访问ES相关接口,会先输出这么一段错误信息:
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.LogManager
at org.elasticsearch.client.RestHighLevelClient.(RestHighLevelClient.java:261)
于是瞬间,将焦点聚集在Log4j上。根据之前多次遇到类似的问题,这里很明显是log4j和logback的冲突。
进一步点开pom依赖证实,发现应用比其他应用多了:log4j-core、log4j-slf4j-impl 两个包。
瞬间豁然。表象上看是ES的RestHighLevelClient 类不存在,但是该类之所以不存在是因为,JVM第一次加载RestHighLevelClient class到内存中来时,由于class也要有初始化动作,该类有一个private static 的logger属性需要初始化,由于log4j和logback的冲突,导致该变量无法初始化,进一步导致RestHighLevelClient class 无法被加载成功。
line 261:private static final Logger logger = LogManager.getLogger(RestHighLevelClient.class);
程序上第二次、第三次、第N次访问接口时,报错就只报
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.elasticsearch.client.RestHighLevelClient
让人误以为是ES包冲突。
解决该问题,必须要对jvm 类加载机制有一定了解。
总结
直接原因
程序执行时创建ES 连接RestHighLevelClient 时,报该类不存在。
根本原因
hive-jdbc 升级版本到2.3.3后,间接依赖的log4j-core、log4j-slf4j-impl两个包和logback 的包有冲突。将引入log4j-core、log4j-slf4j-impl两个jar包排掉就可以了。
- 真正错误原因分析
关键错误信息:
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.LogManager
at org.elasticsearch.client.RestHighLevelClient.<clinit>(RestHighLevelClient.java:261)
at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClient.restHighLevelClient(ElasticSearchRestClient.java:128)
at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClient.<init>(ElasticSearchRestClient.java:78)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.LogManager
at org.elasticsearch.client.RestHighLevelClient.(RestHighLevelClient.java:261)
这段核心错误的关键在于at org.elasticsearch.client.RestHighLevelClient.
clint是指jvm加载类初始化时,发生的错误。
JVM第一次加载RestHighLevelClient class到内存中来时,由于class初始化动作需要初始化static变量,该类有一个private static 的logger属性需要初始化,由于log4j和logback的冲突,导致该变量无法初始化,进一步导致RestHighLevelClient class 无法被加载成功。
- 源码截图
解决方法
排除冲突的log4j-core、log4j-slf4j-impl两个jar包
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</exclusion>
<exclusion>
<artifactId>*</artifactId>
<groupId>com.sun.jersey</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-core</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-slf4j-impl</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
</exclusions>
</dependency>