1. 异常
在使用springboot2.2.8+elasticsearch6.8.10时,测试时报错:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchClient' defined in class path resource [org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.transport.TransportClient]: Factory method 'elasticsearchClient' threw exception; nested exception is java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]
2. 原因
- Elasticsearch 和 Redis 底层都使用到了 Netty , 在项目启动时会冲突。
涉及到的类:NettyRuntime,Netty4Util。
- 查看NettyRuntime类,可以看下源码:
public final class NettyRuntime {
private static final NettyRuntime.AvailableProcessorsHolder holder = new NettyRuntime.AvailableProcessorsHolder();
public static void setAvailableProcessors(int availableProcessors) {
holder.setAvailableProcessors(availableProcessors);
}
public static int availableProcessors() {
return holder.availableProcessors();
}
private NettyRuntime() {
}
static class AvailableProcessorsHolder {
private int availableProcessors;
AvailableProcessorsHolder() {
}
synchronized void setAvailableProcessors(int availableProcessors) {
ObjectUtil.checkPositive(availableProcessors, "availableProcessors");
// 简单说明:在项目启动时,redis自动设置好Netty处理器(availableProcessors就不为0),而此时elasticsearch也启动,发现Netty处理器已经被设置好了(发现availableProcessors!=0)然后会报异常。无论哪一个先启动,都会有判断去报这样的异常
if (this.availableProcessors != 0) {
// 看到这一句:跟上面报错的格式是一样的
String message = String.format(Locale.ROOT, "availableProcessors is already set to [%d], rejecting [%d]", this.availableProcessors, availableProcessors);
throw new IllegalStateException(message);
} else {
this.availableProcessors = availableProcessors;
}
}
@SuppressForbidden(
reason = "to obtain default number of available processors"
)
synchronized int availableProcessors() {
if (this.availableProcessors == 0) {
int availableProcessors = SystemPropertyUtil.getInt("io.netty.availableProcessors", Runtime.getRuntime().availableProcessors());
this.setAvailableProcessors(availableProcessors);
}
return this.availableProcessors;
}
}
}
而 Elasticsearch 底层使用了 Netty4Util ,这个类调用NettyRuntime的方法:
public static void setAvailableProcessors(final int availableProcessors) {
// we set this to false in tests to avoid tests that randomly set processors from stepping on each other
// 而这里就是解决办法:
final boolean set = Booleans.parseBoolean(System.getProperty("es.set.netty.runtime.available.processors", "true"));
if (!set) {
return;
}
/*
* This can be invoked twice, once from Netty4Transport and another time from Netty4HttpServerTransport; however,
* Netty4Runtime#availableProcessors forbids settings the number of processors twice so we prevent double invocation here.
*/
if (isAvailableProcessorsSet.compareAndSet(false, true)) {
// 看着,回去调用NettyRuntime的setAvailableProcessors
NettyRuntime.setAvailableProcessors(availableProcessors);
} else if (availableProcessors != NettyRuntime.availableProcessors()) {
/*
* We have previously set the available processors yet either we are trying to set it to a different value now or there is a bug
* in Netty and our previous value did not take, bail.
*/
// 看下面的格式跟报错的格式一样
final String message = String.format(
Locale.ROOT,
"available processors value [%d] did not match current value [%d]",
availableProcessors,
NettyRuntime.availableProcessors());
throw new IllegalStateException(message);
}
}
3. 解决
- 在Netty4Util源码可以看到,配置es.set.netty.runtime.available.processors设置为false就不会去检查Netty处理器是否配置。
- 因为得启动的时候就得去解决冲突,所以设置在 启动类那
@SpringBootApplication
public class XXXApplication {
@PostConstruct
public void init() {
// 解决netty启动冲突的问题(主要体现在启动redis和elasticsearch)
// 可以看Netty4Util.setAvailableProcessors(..)
System.setProperty("es.set.netty.runtime.available.processors", "false");
}
public static void main(String[] args) {
SpringApplication.run(CommunityApplication.class, args);
}
}
- 配置在main函数,调用run方法前也可以。
这个问题搞我一天时间,当时是直接copy Netty4Util类的System… , 没发现它是getProperty,导致还是出现这个异常,我真的不知道咋解决了。所以要看清楚,这里是setProperty
4. 其他注意点
一定要让本地的ElasticSearch跟SpringBoot集成的es版本保持一致。否则可能会出错。特别是一个使用6版本和一个使用7版本。
Maven可以看SpringBoot集成的es版本:
如果需要改就在这里:pom中
<properties>
<java.version>1.8</java.version>
<!--定义elasticsearch版本依赖,保证跟本地版本一致,否则可能出错-->
<!-- <elasticsearch.version>7.6.2</elasticsearch.version>-->
</properties>
7版本不太熟,改动也很大,以后有时间再升级。