SpringBoot集成ElasticSearch出现的异常

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版本不太熟,改动也很大,以后有时间再升级。

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页