Как обойти «сброс соединения по одноранговым узлам» при использовании Elasticsearch RestClient - PullRequest
0 голосов
/ 25 октября 2018

Мы используем Hibernate Search 5.10.3. Финал с сервером Elasticsearch 5.6.6.

Связь между нашим приложением и ES кажется надежной при прямой выдаче FullTextQueries, возможно, в b / c HibernateSearch есть некоторые встроенные повторыМетод, я не уверен, однако, также в нашем приложении мы используем RestClient Elasticsearch для прямого вызова _analyze, здесь мы получаем connection reset by peer IOException, когда наш брандмауэр закрывает незанятые соединения через 30 минут.

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);
}

Мы попытались создать «сердцебиение», которое вызывает небольшой вызов «_cluster / health»использование RestClient каждую минуту, но это, похоже, тоже не решает проблему полностью.Даже сердцебиение иногда прерывается с тем же IOException.

  1. Может ли кто-нибудь объяснить количество соединений между HibernateSearch и ES (я думал, что по умолчанию оно равно 20 или 2 в зависимости от кластеризации ES или нет), и еслисоединения используются в циклическом или случайном порядке?
  2. Будет ли простая повторная попытка вызова RestClient снова разбудить соединение?
  3. Или нам нужно вручную повторно подключить соединение к ES, и если да, то как?
  4. Наконец, существует ли существующая настройка поиска в спящем режиме, которая может решить эту проблему, возможно, hibernate.search.default.elasticsearch.discovery.enabled или другую?

1 Ответ

0 голосов
/ 26 октября 2018

Объяснение проблемы

Я предполагаю, что ваше объяснение о закрытии соединения через брандмауэр через 30 минут является правильным.

Из того, что я вижу, ApacheHTTP-клиент решает, как долго поддерживать текущее соединение, основываясь на ConnectionKeepAliveStrategy.По умолчанию это org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy, и это будет поддерживать живое соединение до тех пор, пока это рекомендуется заголовком Keep-Alive в ответах от сервера Elasticsearch, или неопределенно долго, если сервер Elasticsearch не возвращает такой заголовок в ответах.

Я провел несколько тестов, и, по-видимому, Elasticsearch не возвращает ни одного заголовка Keep-Alive, поэтому в настоящее время соединения повторно используются бесконечно, по крайней мере до тех пор, пока ваша сеть их не убьет.

После подключенияЕсли вы убиты, вы можете надеяться, что вступят в силу автоматические повторные попытки, но они эффективны, только если у вас более одного узла Elasticsearch.Если у вас есть только один узел, и запрос не выполняется, тогда остальной клиент не будет повторять попытку на том же узле.

Таким образом, в общем, ожидаются сбои.Что не так, тот факт, что вы были свидетелями сбоев только с вашим собственным клиентским кодом, но я думаю, что вы, возможно, пропустили некоторые ошибки в журналах?

Решение (надеюсь)

Может быть,HTTP-клиент Apache может автоматически обрабатывать повторное открытие соединений, когда они принудительно закрыты, но я не смог найти такую ​​функцию.

Я не смог найти способ заставить сервер Elasticsearch добавить заголовок Keep-Aliveк его ответам HTTP, либо.

Если вы используете HTTP, а не HTTPS (в этом случае я надеюсь, что это частная сеть), вы сможете настроить сетевую инфраструктуру для вставки таких заголовков в каждое сообщение HTTP,Если вы используете Elasticsearch за прокси-сервером, таким как сервер Apache, вы также должны это сделать.

В противном случае, чтобы явно настроить его на стороне клиента, вы можете использовать расширение org.hibernate.search.elasticsearch.client.spi.ElasticsearchHttpClientConfigurerточка в Hibernate Search.

WARNING : эта точка расширения является 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 или нет) и если соединения используются в циклическом или случайном порядке?

Из документации:

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 соединений.

Будет ли простая повторная попытка вызова RestClient "разбудить" соединение снова?

Насколько я знаю, это должно произойти, но тогда я не знаю, что именно сбрасываетваша связь, поэтому трудно сказать.

Или нам нужно вручную повторно подключить соединение к ES, и если да, то как?

Не думаю, что вам следует делать это самостоятельно.Соединения управляются автоматически на очень низком уровне.Не Hibernate Search, даже не Rest Client, а HTTP-клиентом.

В любом случае, если вы действительно хотите пойти по этому пути, вам придется каким-то образом заполучить HTTP-клиента.Я не знаю как.

Наконец, существует ли существующий параметр поиска в режиме гибернации, который мог бы решить эту проблему, возможно hibernate.search.default.elasticsearch.discovery.enabled или другой?

hibernate.search.default.elasticsearch.discovery.enabled только поможетесли вам нужно больше соединений и ваш Elasticsearch кластеризован;в вашем случае кажется, что ваши существующие соединения прерываются через определенное время, поэтому даже если вы увеличите количество соединений, вы все равно столкнетесь с той же проблемой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...