使用Elasticsearch的RestClient时如何解决“对等连接重置”

时间:2018-10-25 20:34:46

标签: java elasticsearch hibernate-search

我们使用的是Hibernate Search 5.10.3.Final针对Elasticsearch 5.6.6服务器。

直接发出FullTextQueries时,我们的应用程序和ES之间的连接似乎很牢固,也许b / c HibernateSearch具有一些内置的重试方法,但是我不确定,但是,在我们的应用程序中,我们使用Elasticsearch的RestClient发出了一个直接调用_analyze,这是我们的防火墙在30分钟后关闭空闲连接时得到的connection reset by peer IOException。

java.io.IOException: Connection reset by peer
    at sun.nio.ch.FileDispatcherImpl.read0(Native Method) ~[?:1.8.0_131]
    at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39) ~[?:1.8.0_131]
    at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223) ~[?:1.8.0_131]
    at sun.nio.ch.IOUtil.read(IOUtil.java:197) ~[?:1.8.0_131]
    at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380) ~[?:1.8.0_131]
    at org.apache.http.impl.nio.reactor.SessionInputBufferImpl.fill(SessionInputBufferImpl.java:204) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.codecs.AbstractMessageParser.fillBuffer(AbstractMessageParser.java:136) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:241) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81) ~[httpasyncclient-4.1.2.jar:4.1.2]
    at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39) ~[httpasyncclient-4.1.2.jar:4.1.2]
    at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:588) ~[httpcore-nio-4.4.5.jar:4.4.5]
    at java.lang.Thread.run(Thread.java:748) ~[?:1.8.0_131]

为完整起见,这是我们大多数的RestClient代码:

SearchFactory searchFactory = fts.getSearchFactory();
IndexFamily indexFamily = searchFactory.getIndexFamily(ElasticsearchIndexFamilyType.get());
ElasticsearchIndexFamily elasticsearchIndexFamily = indexFamily.unwrap(ElasticsearchIndexFamily.class);
RestClient restClient = elasticsearchIndexFamily.getClient(RestClient.class);

Map<String, String> rawData = new HashMap<>();
rawData.put("analyzer", analyzer);
rawData.put("text", text);

try {
    String jsonData = objectMapper.writeValueAsString(rawData);
    HttpEntity entity = new NStringEntity(jsonData, ContentType.APPLICATION_JSON);

    Response response = restClient.performRequest("GET", "vendor/_analyze", Collections.emptyMap(), entity);

    int statusCode = response.getStatusLine().getStatusCode();
    if (statusCode == HttpStatus.SC_OK) {
        // we parse the response here
    }
} catch (IOException e) {
    String message = "Error communicating with Elasticsearch!";
    logger.error(message, e);
    throw new IllegalStateException(message, e);
}

我们尝试创建一个“心跳”,每分钟使用RestClient发出一个小的“ _cluster / health”调用,但这似乎也不能完全解决问题。甚至心跳有时也会因相同的IOException而失败。

  1. 有人可以解释HibernateSearch与ES之间的连接数(我认为默认为20或2,取决于是否将ES集群化了),以及这些连接是以循环方式还是随机方式使用的?
  2. 简单重试RestClient会再次“唤醒”连接吗?
  3. 还是我们需要手动将连接重新连接到ES?如果是,怎么办?
  4. 最后,是否有一个现有的休眠搜索设置可以解决此问题,可能是hibernate.search.default.elasticsearch.discovery.enabled或另一个?

1 个答案:

答案 0 :(得分:2)

问题的说明

我假设您对30分钟后防火墙关闭连接的解释是正确的。

据我所见,Apache HTTP客户端根据ConnectionKeepAliveStrategy决定给定连接保持活动的时间。默认情况下,它是org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy,这将使连接保持活动状态,直到Elasticsearch服务器的响应中Keep-Alive头建议的时间,或者无限期(如果Elasticsearch服务器未在其中返回此类头)。回应。

我做了一些测试,显然Elasticsearch不会返回任何Keep-Alive头,因此当前,连接可以无限期地重复使用,至少直到您的网络杀死它们为止。

一旦连接被杀死,您可以希望自动重试进入,但是只有在您有多个Elasticsearch节点时,它们才有效。如果您只有一个节点并且请求失败,那么其余客户端将不会在同一节点上重试。

因此,总的来说,失败是可以预期的。什么不是,是您仅用自己的客户端代码见证了失败,但是我想您可能忽略了日志中的某些错误?

解决方案(希望如此)

也许Apache HTTP客户端可以在强制关闭连接时自动处理重新打开的连接,但是我找不到这种功能。

我也找不到使Elasticsearch服务器在其HTTP响应中添加Keep-Alive头的方法。

如果使用HTTP而不是HTTPS(在这种情况下,我希望它是专用网络),则可以配置网络基础结构以在每个HTTP消息中插入此类标头。如果在诸如Apache服务器之类的代理后面使用Elasticsearch,那么您也应该能够这样做。

否则,为了在客户端上显式配置它,可以在Hibernate Search中使用org.hibernate.search.elasticsearch.client.spi.ElasticsearchHttpClientConfigurer扩展点。

警告:此扩展点是SPI,最重要的是,它是实验性的,这意味着在任何较新版本的Hibernate Search中,它都可能以不兼容的方式进行更改。在下一次升级中,即使是微升级,也可能必须更改代码。我们没有保证。

创建一个实现:

package com.acme.config;

import org.hibernate.search.elasticsearch.client.spi.ElasticsearchHttpClientConfigurer;

public class MyHttpConfigurer implements ElasticsearchHttpClientConfigurer {
   private static final int KEEP_ALIVE_MS = 20 * 60 * 1000; // 20 minutes
    @Override
    public void configure(HttpAsyncClientBuilder builder, Properties properties) {
        builder.setKeepAliveStrategy( (response, context) -> KEEP_ALIVE_MS );
    }
}

通过创建具有以下内容的META-INF/services/org.hibernate.search.elasticsearch.client.spi.ElasticsearchHttpClientConfigurer文件来注册您的实现:

com.acme.config.MyHttpConfigurer

...您就完成了。

在调试模式下使用MyHttpConfigurer中的断点启动应用程序一次以检查其是否已执行,如果已执行,则HTTP客户端应在20分钟后自动停止使用空闲连接,并且您不会遇到相同的问题再次。

回答您的问题

  
      
  1. 有人可以解释HibernateSearch与ES之间的连接数(我认为默认为20或2,取决于是否将ES集群化了),以及这些连接是以循环方式还是随机方式使用的?
  2.   

从文档中:

  

hibernate.search.default.elasticsearch.max_total_connection 20(默认)

     

hibernate.search.default.elasticsearch.max_total_connection_per_route 2(默认)

它不取决于ES是否集群。这取决于客户端知道多少个节点/路由。如果禁用自动发现(默认为hibernate.search.default.elasticsearch.discovery.enabled false,则客户端已知的节点是您显式配置的节点。如果启用了该功能,并且群集中有多个节点,则客户端可能知道的节点数比您显式配置的要多。

默认情况下,客户端已知每个主机最多使用两个连接,但总数不得超过20。因此,如果知道9个节点,则最多使用18个连接,如果知道10个节点,则最多使用20个连接,如果知道11个或更多节点,则仍然最多使用20个连接。

  
      
  1. 简单重试RestClient会再次“唤醒”连接吗?
  2.   

据我所知,它应该,但是后来我不知道是什么完全重置了您的连接,所以很难说。

  
      
  1. 还是我们需要手动将连接重新连接到ES?如果是,怎么办?
  2.   

我不认为您应该自己这样做。连接会在非常低的级别自动进行管理。不是通过Hibernate Search,甚至不是由Rest Client,而是由HTTP Client。

无论如何,如果您真的想那样做,则必须以某种方式使用HTTP客户端。我不知道。

  
      
  1. 最后,是否存在可以解决此问题的现有休眠搜索设置,可能是hibernate.search.default.elasticsearch.discovery.enabled或其他?
  2.   

hibernate.search.default.elasticsearch.discovery.enabled仅在需要更多连接且您的Elasticsearch已集群时才有用。在您的情况下,似乎您的现有连接会在一段时间后被杀死,因此即使您增加连接数,您仍然会遇到相同的问题。