Elasticsearch Java High-Level REST Client 建立一堆 TCP 连接并且在索引数据后不关闭它们

Posted

技术标签:

【中文标题】Elasticsearch Java High-Level REST Client 建立一堆 TCP 连接并且在索引数据后不关闭它们【英文标题】:Elasticsearch Java High-Level REST Client establish a bunch of TCP connection and doesn't close them after indexing data 【发布时间】:2019-02-18 11:58:12 【问题描述】:

我有一个每秒运行一次的周期性作业(这是可配置的)。

在这个工作中,我首先创建一个到 Elasticsearch 服务器的连接:

RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(new HttpHost(address, port, "http")));

然后我检查是否存在名为test 的特殊索引。如果它不存在,我先创建它。

GetIndexRequest indexRequest = new GetIndexRequest();
indexRequest.indices("test");
boolean testIndexIsExists = false;
try            
     testIndexIsExists = client.indices().exists(indexRequest, RequestOptions.DEFAULT); 
     catch (IOException ioe) 
    logger.error("Can't check the existence of test index in Elasticsearch!");  

if(testIndexIsExists) 
     // bulk request...
 else 
    CreateIndexRequest testIndex = new CreateIndexRequest("test");
    try    
        testIndex.mapping("doc", mappingConfiguration);
        client.indices().create(testIndex, RequestOptions.DEFAULT);
        // bulk request...  
     catch (IOException ioe)  
        logger.error("Can't create test index in Elasticsearch");
       

在执行了包含近 2000 个文档的批量请求后,我关闭了 Elasticsearch 客户端连接:

client.close();

Java 高级 REST 客户端版本:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>6.4.0</version>
</dependency>

我的问题是一堆 TCP 连接已经建立并且没有关闭。这些 TCP 连接会随着时间的推移占用所有操作系统 TCP 连接。

另一方面,我有点困惑。 RestHighLevelClient 实例应该是整个应用程序的单例对象,还是我必须在每个作业运行周期中创建一个新实例并在完成该作业后关闭该实例?

【问题讨论】:

您是否在应用程序中一次使用多个客户端实例?因为你应该只使用一个并且一直使用那个。然后在您的应用退出时close。它应该自己处理所有其他事情。 您使用的弹性和客户端的确切版本是什么? Elasticsearch:6.6.1,Java 高级 REST 客户端:6.4.0 为什么不保留客户端实例而不是每次都创建新的? 应用程序终止后连接是否清除? 【参考方案1】:

高级客户端已经为您维护了一个连接池,所以我会将它用作单例。不断创建和关闭连接池的成本很高,而且客户端和底层 HTTP 连接池都是线程安全的。此外,在客户端上调用 close() 只是委托给 Apache HTTP 客户端 shutdown() 方法,因此您将受制于它们如何处理清理和释放资源。

如果您使用 Spring 或其他一些 DI 框架,则可以轻松创建可以根据需要注入的客户端单例实例。您可以在 bean 关闭/销毁生命周期阶段添加对 client.close() 的调用。

使用 Spring Boot 的快速示例:

@Configuration
@ConditionalOnClass(RestHighLevelClient.class)
public class ElasticSearchConfiguration 

    @Value("$elasticsearch.address")
    String address;

    @Value("$elasticsearch.port")
    int port;

    @Bean(destroyMethod = "close")
    public RestHighLevelClient restHighLevelClient() 
        return new RestHighLevelClient(
                RestClient.builder(new HttpHost(address, port, "http")));
    

注意:在这种情况下,Spring 会自动检测到 bean 有一个close 方法,并在 bean 被销毁时为您调用它。其他框架可能要求您指定应如何处理关闭。

【讨论】:

【参考方案2】:

RestHighLevelClient 通常应该是单例的,除非你有充分的理由。例如,如果您的作业每小时而不是一分钟运行一次,那么创建新实例并在作业结束后关闭它可能是有意义的。

如果您确定在所有情况下都调用close()(例如,您没有错过任何异常),那么我的下一个猜测是弹性客户端中的错误。

看起来他们忘记在 exists 调用中使用响应: https://github.com/elastic/elasticsearch/blob/v6.4.0/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java#L1419

你能在没有exists 调用的情况下进行测试吗?

【讨论】:

以上是关于Elasticsearch Java High-Level REST Client 建立一堆 TCP 连接并且在索引数据后不关闭它们的主要内容,如果未能解决你的问题,请参考以下文章

java怎么给elasticsearch插入数据

ElasticSearch JVM配置

java SQL查询elasticsearch?

java elasticsearch操作: 连接elasticsearch8.x

ElasticSearch-学习笔记04Java客户端操作索引库

Java连接Elasticsearch集群