Elasticsearch7.9.2与Nebula Spark连接器冲突RestHighLevelClient实例化失败
问题背景
- 当前运行环境使用Elasticsearch 7.9.2版本,集成Nebula Graph官方Spark连接器
nebula-spark-connector时出现依赖冲突 - 此前了解过Spark与Elasticsearch依赖冲突的相关案例,但未找到可直接落地的解决方案
- 已尝试对
nebula-spark-connector的jar包执行shade重打包处理,报错未消除
报错信息
服务启动初始化ES高级REST客户端时抛出如下错误:
Failed to instantiate [org.elasticsearch.client.RestHighLevelClient]: Factory method 'restHighLevelClient' threw exception; nested exception is java.lang.NoSuchFieldError: INSTANCE at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ... 47 common frames omitted Caused by: java.lang.NoSuchFieldError: INSTANCE at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager$InternalAddressResolver.<init>(PoolingNHttpClientConnectionManager.java:619) at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.<init>(PoolingNHttpClientConnectionManager.java:165) at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.<init>(PoolingNHttpClientConnectionManager.java:149) at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.<init>(PoolingNHttpClientConnectionManager.java:121) at org.apache.http.impl.nio.client.HttpAsyncClientBuilder.build(HttpAsyncClientBuilder.java:668) at java.security.AccessController.doPrivileged(Native Method) at org.elasticsearch.client.RestClientBuilder.createHttpClient(RestClientBuilder.java:219) at java.security.AccessController.doPrivileged(Native Method) at org.elasticsearch.client.RestClientBuilder.build(RestClientBuilder.java:191) at org.elasticsearch.client.RestHighLevelClient.<init>(RestHighLevelClient.java:287) at org.elasticsearch.client.RestHighLevelClient.<init>(RestHighLevelClient.java:279) at com.bybit.byassets.collection.config.ElasticSearchConfig.restHighLevelClient(ElasticSearchConfig.java:54) at com.bybit.byassets.collection.config.ElasticSearchConfig$$EnhancerBySpringCGLIB$$e050e7f6.CGLIB$restHighLevelClient$0(<generated>) at com.bybit.byassets.collection.config.ElasticSearchConfig$$EnhancerBySpringCGLIB$$e050e7f6$$FastClassBySpringCGLIB$$43cd2bed.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) at com.bybit.byassets.collection.config.ElasticSearchConfig$$EnhancerBySpringCGLIB$$e050e7f6.restHighLevelClient(<generated>) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ... 48 common frames omitted
根因说明
这个NoSuchFieldError: INSTANCE是典型的依赖版本不匹配问题,冲突点在Apache HttpAsyncClient相关包:
- Elasticsearch 7.9.2 对http依赖的版本要求固定:
httpasyncclient:4.1.4、httpcore-nio:4.4.14、httpclient:4.5.13,这些版本中PoolingNHttpClientConnectionManager$InternalAddressResolver类依赖的INSTANCE静态字段存在 nebula-spark-connector会传递引入版本不匹配的http相关依赖,类加载时优先加载了错误版本的类,就会报字段不存在的错误- 之前shade重打包没生效,基本是因为配置没有覆盖全所有http相关包,要么没做类路径重定位,要么打包后错误版本的类还是从更高优先级的classpath路径被加载了
可落地解决方案
方案1:依赖排除+版本锁定(优先使用,改造成本最低)
直接在项目构建配置中排除nebula-spark-connector传递进来的所有http相关依赖,全局锁定为ES 7.9.2匹配的版本。
如果是Maven项目,在引入connector的依赖块中添加排除规则:
<dependency> <groupId>com.vesoft</groupId> <artifactId>nebula-spark-connector</artifactId> <version>${你实际使用的connector版本}</version> <exclusions> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId> </exclusion> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> </exclusion> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore-nio</artifactId> </exclusion> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </exclusion> </exclusions> </dependency>
然后在dependencyManagement节点全局锁定http依赖版本:
<dependencyManagement> <dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId> <version>4.1.4</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.14</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore-nio</artifactId> <version>4.4.14</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> </dependencies> </dependencyManagement>
如果是Spark集群提交作业,额外加两个提交参数,让用户jar包的类优先被加载,避免集群内置的旧版本依赖抢占优先级:
--conf spark.driver.userClassPathFirst=true --conf spark.executor.userClassPathFirst=true
方案2:修正shade重打包配置
如果必须用shade方式处理依赖,要在maven-shade-plugin的配置中补全http包的重定位规则,把connector自带的http类全部重写到独立路径下,和ES依赖的http包隔离开:
<relocation> <pattern>org.apache.http</pattern> <shadedPattern>shaded.nebula.org.apache.http</shadedPattern> </relocation>
注意:这个规则必须覆盖所有org.apache.http开头的包路径,漏了任何一个http相关的子包都会导致冲突复现
方案3:类加载隔离(适合复杂集群环境)
如果集群环境依赖复杂,前两种方案都不生效,可以把nebula-spark-connector相关依赖放在独立目录,通过自定义类加载器加载connector相关类,和ES客户端的类加载器完全隔离,不过这个方案改造成本较高,非必要不选。
验证步骤
修改配置后,执行依赖树检查命令,确认整个依赖树中没有其他版本的http相关包:
- Maven项目执行:
mvn dependency:tree | grep http - Gradle项目执行:
gradle dependencies | grep http
确认所有http依赖版本都和ES 7.9.2要求的版本一致后,再启动服务即可。
内容的提问来源于stack exchange,提问作者sim
相关产品推荐
相关产品推荐

