【问题标题】:OutOfMemoryError with elasticsearch REST Java client via apache http nio通过 apache http nio 使用 elasticsearch REST Java 客户端的 OutOfMemoryError
【发布时间】:2017-06-21 13:09:55
【问题描述】:

我们正在使用 elasticsearch REST Java 客户端(我们使用的是 Java 7,因此无法使用普通的 elasticsearch Java 客户端)与我们的 elasticsearch 服务器进行交互。除了我们尝试对大约 130 万个文档进行初始索引时,这一切都很好。这运行了一段时间,但在几十万个文档之后,我们得到了一个

20/06 21:27:33,153 ERROR [cid=51][stderr][write:71] (pool-837116-thread-1) Exception in thread "pool-837116-thread-1" java.lang.OutOfMemoryError: unable to create new native thread
20/06 21:27:33,154 ERROR [cid=51][stderr][write:71] (pool-837116-thread-1)  at java.lang.Thread.start0(Native Method)
20/06 21:27:33,154 ERROR [cid=51][stderr][write:71] (pool-837116-thread-1)  at java.lang.Thread.start(Thread.java:693)
20/06 21:27:33,154 ERROR [cid=51][stderr][write:71] (pool-837116-thread-1)  at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:334)
20/06 21:27:33,154 ERROR [cid=51][stderr][write:71] (pool-837116-thread-1)  at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.execute(PoolingNHttpClientConnectionManager.java:194)
20/06 21:27:33,154 ERROR [cid=51][stderr][write:71] (pool-837116-thread-1)  at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase$1.run(CloseableHttpAsyncClientBase.java:64)
20/06 21:27:33,155 ERROR [cid=51][stderr][write:71] (pool-837116-thread-1)  at java.lang.Thread.run(Thread.java:724)

紧随其后

java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED
    at org.apache.http.util.Asserts.check(Asserts.java:46)
    at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase.ensureRunning(CloseableHttpAsyncClientBase.java:90)
    at org.apache.http.impl.nio.client.InternalHttpAsyncClient.execute(InternalHttpAsyncClient.java:123)
    at org.elasticsearch.client.RestClient.performRequestAsync(RestClient.java:343)
    at org.elasticsearch.client.RestClient.performRequestAsync(RestClient.java:325)
    at org.elasticsearch.client.RestClient.performRequest(RestClient.java:218)
    at org.elasticsearch.client.RestClient.performRequest(RestClient.java:191)

如您所见,Elasticsearch REST 客户端正在使用 apache http nio。我发现奇怪的是 nio 库正在为每个请求(或连接?)创建一个线程。从上面的日志中您可以看到线程 (pool-837116-thread-1)。还有很多 I/O 调度程序线程,数量不断增加。

不过,活动线程的总数似乎没有太大变化。 因此,似乎不是重用线程,而是为每个连接周期创建一个(或两个实际上)新线程。 上传基本上是:

1.创建客户端

    restClient = RestClient.builder(new HttpHost(host.getHost(),host.getPort(),host.getProtocol())/*,new HttpHost(host.getHost(),host.getPort()+1,host.getProtocol())*/)
                            .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                                @Override
                                public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                                    return httpClientBuilder
                                            .setDefaultCredentialsProvider(credsProvider)
                                                                                }
                            }).setMaxRetryTimeoutMillis(30000).build();

2。使用 json body 发送请求并关闭客户端

        try{
            HttpEntity entity = new NStringEntity(json,ContentType.APPLICATION_JSON);
            Response indexResponse = restClient.performRequest("PUT", endpoint, parameters,entity,header);
            log.debug("Response #0 #1", indexResponse,indexResponse.getStatusLine());
            log.debug("Entity #0",indexResponse.getEntity());

        }finally{
            if(restClient!=null){
                log.debug("Closing restClient #0", restClient);
                restClient.close();
            }
        }

这正常吗?为什么 apache nio 不重用线程?这是弹性搜索 REST 客户端、apache nio 还是我的代码的问题?我在 restClient 上调用 close,不知道我还应该做什么。

我尝试将 IO Reactor 上的线程数设置为 1:

restClient = RestClient.builder(new HttpHost(host.getHost(),host.getPort(),host.getProtocol())/*,new HttpHost(host.getHost(),host.getPort()+1,host.getProtocol())*/)
                            .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                                @Override
                                public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                                    return httpClientBuilder
                                            .setDefaultCredentialsProvider(credsProvider)
                                            .setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(1).build()); //set to one thread
                                }
                            }).setMaxRetryTimeoutMillis(30000).build();

但这并没有改变线程重用方面的任何事情。

【问题讨论】:

  • 您是使用批量插入还是一对一插入?
  • 我找到了 OutOfMemoryError 的原因。尽管我使用了一个 try - finally 块,在该块中我将关闭客户端 - 在该块之外引发了一个异常(该块没有涵盖所有内容,D'oh)。但是创建这么多线程看起来仍然是错误的(尽管总线程的数量没有显着增加)。
  • 这是一个一对一的插入,因为我需要确保每个插入的数据都已上传。它使用与普通索引相同的机制。
  • 批量插入还会在批处理中的单个记录失败时通知您,因此这可能是一种选择。批量 api 速度更快,资源消耗更少(即使是非常小的批次)
  • 一些 JSON 数据中有换行符(我无法删除)。这仍然适用于批量插入吗?我读过你不应该漂亮地打印 json,因为它使用换行符来分隔命令。跳过引号中的换行符是否足够聪明?

标签: java rest elasticsearch apache-httpasyncclient


【解决方案1】:

我找到了 OutOfMemoryError 的原因。尽管我使用了一个 try - finally 块,在该块中我将关闭客户端 - 在该块之外引发了一个异常(该块没有涵盖所有内容,D'oh)。但是创建这么多线程看起来仍然是错误的(尽管总线程的数量没有显着增加)。

【讨论】:

    【解决方案2】:

    记录一下:我在使用 ElasticSearch 时也遇到了这个错误 IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED

    根本原因在于我的应用程序,它试图多次使用同一个帐户登录,这反过来又导致在各个索引上出现多个不必要的 save() 和 retrieveById() 语句。

    要诊断类似问题,仔细检查堆栈跟踪会有所帮助 - 如果错误发生在多个不同的单元测试中 - 查找那里的任何共性,例如在索引上调用相同的方法。

    【讨论】:

      猜你喜欢
      • 2012-05-13
      • 1970-01-01
      • 1970-01-01
      • 2012-08-22
      • 2017-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      相关资源
      最近更新 更多