那些年踩过的坑-RestHighLevelClient初始化问题分析

下面先简单的介绍一下RestHighLevelClient使用,再看下大家在使用过程中可能踩的坑。

1、springboot如何集成RestHighLevelClient?

2、RestHighLevelClient初始化源码分析

一、springboot如何集成RestHighLevelClient?

1、引入maven依赖

<!--引入es相关依赖-->
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.6.2</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>7.6.2</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.6.2</version>
</dependency>

2、创建RestHighLevelClient实例对象

RestClientBuilder builder = RestClient.builder(
                //单机部署
                new HttpHost("127.0.0.1", 9200, "http")
        );
RestHighLevelClient highLevelClient = new RestHighLevelClient(builder);

3、通过client实例访问ES

//1.创建 SearchRequest搜索请求
SearchRequest searchRequest = new SearchRequest();
//指定要查询的索引
searchRequest.indices("user");
//2.创建 SearchSourceBuilder条件构造。builder模式这里就先不简写了
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
searchSourceBuilder.query(matchAllQueryBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("查询响应结果: " + searchResponse);

通过以上三步即可简单的访问ES服务。

二、初始化RestHighLevelClient的坑

对于初始化restHighLevelClient的正常流程来说只需要在spring容器启动后,使用@Bean注解实例化一个restHighLevelClient对象,然后在不用的service中@Autowire注入即可使用。

那有没有想过为什么正常只推荐单例模式就可以了?如果我每次使用restHighLevelClient都去new一个会出现什么问题?

下面我们就简单的分析下restHighLevelClient的原理。

先说一下结论

restHighLevelClient整体流程采取的是NIO+Reactor模式来处理网络IO通信问题关于NIO和Reactor模式可以看我另一篇介绍Netty的文章),而且在这个的基础上增加了一个池,通过池一方面可以弹性配置连接的大小,另一方面可以客户端做一些实现来对连接池的状态进行监控。所以在Elasticsearch的restClient使用中,为什么使用单例而且能保持比较高的性能。

如何验证?

我们可以建一个springboot项目demo,在项目启动后实例化一个restHighLevelClient对象,然后用jdk自带的jvm监控工具进行分析。

@SpringBootApplication
public class SpringLearningApplication implements ApplicationListener<ApplicationReadyEvent> {

    public static void main(String[] args) {
        SpringApplication.run(SpringLearningApplication.class, args);
    }

    // 应用启动后创建实例化一个highLevelClient
    @SneakyThrows
    @Override
    public void onApplicationEvent(@NotNull ApplicationReadyEvent event) {
        RestClientBuilder builder = RestClient.builder(
                //单机部署
                new HttpHost("127.0.0.1", 9200, "http")
        );
        Thread.sleep(60000);
        RestHighLevelClient highLevelClient = new RestHighLevelClient(builder);
    }
}

应用启动后,我们通过jvisualvm连接上应用可以看到还未初始化restHighLevelClient时I/O dispatcher线程只有4个。

 new RestHighLevelClient(builder)执行之后I/O dispatcher线程变成8个。

从图中我们直观的可以看到线程数的变化,接下来从RestHighLevelClient构造方法的源码进入逐步分析以下为什么这里默认创建了4个线程,这个跟什么有关?

  • RestHighLevelClient构造方法

他的核心在restClientBuilder.build()创建RestClient。

创建CloseableHttpAsyncClient过程中如果没有配置IOReactorConfig,则采用默认的IOReactorConfig.DEFAULT 中创建的默认的ioThreadCount为cpu核心数

因此我们在使用RestHighLevelClient时连接一个ES服务只需要创建一个RestHighLevelClient实例。如果有需要可以自己按照如下方式设置下线程池大小,正常业务不是并发量特别大的场景,直接默认的配置即可。

restClientBuilder.setHttpClientConfigCallback(
        new RestClientBuilder.HttpClientConfigCallback() {
            @Override
            public HttpAsyncClientBuilder customizeHttpClient(
                    HttpAsyncClientBuilder httpClientBuilder) {
                return httpClientBuilder.setDefaultIOReactorConfig(
                        IOReactorConfig.custom()
                                .setIoThreadCount(10) // 设置线程数
                                .build());
            }
        });

如果每次使用时都去new,造成的结果就是应用线程数不断增长,很可能抛出OutOfMemoryError异常,且不能提供正常的服务。

// 测试循环new RestHighLevelClient
for (int i = 0; i < 10000000; i++) {
    RestHighLevelClient highLevelClient = new RestHighLevelClient(builder);
    Thread.sleep(200);
}

 总结

大家在引入RestHighLevelClient连接同一个ES服务使用单例模式!

  • 52
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值