Подтвердить что ты не робот

Apache HttpClient Промежуточная ошибка: NoHttpResponseException

У меня есть веб-сервис, который принимает метод POST с XML. Затем он работает нормально, в некоторый случайный случай, он не может связаться с сервером, выдавая IOException с сообщением The target server failed to respond. Последующие звонки работают нормально.

В основном это происходит, когда я делаю несколько звонков, а затем оставляю свое приложение без дела около 10-15 минут. первый звонок, который я делаю после этого, возвращает эту ошибку.

Я попробовал пару вещей...

Я настраиваю обработчик повторов как

HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {

            public boolean retryRequest(IOException e, int retryCount, HttpContext httpCtx) {
                if (retryCount >= 3){
                    Logger.warn(CALLER, "Maximum tries reached, exception would be thrown to outer block");
                    return false;
                }
                if (e instanceof org.apache.http.NoHttpResponseException){
                    Logger.warn(CALLER, "No response from server on "+retryCount+" call");
                    return true;
                }
                return false;
            }
        };

        httpPost.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);

но эта повторная попытка не была вызвана. (да, я использую правильное предложение instanceof). При отладке этот класс никогда не вызывается.

Я даже пытался настроить HttpProtocolParams.setUseExpectContinue(httpClient.getParams(), false); но бесполезно. Может кто-нибудь подсказать, что я могу сделать сейчас?

ВАЖНО Помимо выяснения, почему я получаю исключение, одна из важных проблем, с которыми я сталкиваюсь, заключается в том, почему здесь не работает retryhandler?

4b9b3361

Ответ 1

Скорее всего, постоянные соединения, которые поддерживаются менеджером соединений, становятся устаревшими. То есть целевой сервер отключает соединение на своем конце, без того, что HttpClient может реагировать на это событие, тогда как соединение находится в режиме ожидания, что делает соединение полузакрытым или "устаревшим". Обычно это не проблема. HttpClient использует несколько методов для проверки достоверности соединения при его аренде из пула. Даже если проверка устаревшего соединения отключена и для передачи сообщения запроса используется устаревшее соединение, выполнение запроса обычно выходит из строя в операции записи с помощью SocketException и автоматически возвращается к нему. Однако при некоторых обстоятельствах операция записи может завершаться без исключения, а последующая операция чтения возвращает -1 (конец потока). В этом случае у HttpClient нет другого выбора, кроме как считать запрос успешным, но сервер не смог ответить скорее всего из-за непредвиденной ошибки на стороне сервера.

Самый простой способ исправить ситуацию - выселить истекшие соединения и соединения, которые простаивали дольше, чем, скажем, 1 минута из пула после периода бездействия. Подробнее см. в этом разделе урока HttpClient.

Ответ 2

Принятый ответ правильный, но отсутствует решение. Чтобы избежать этой ошибки, вы можете добавить setHttpRequestRetryHandler (или setRetryHandler для компонентов Apache 4.4) для вашего HTTP-клиента, например, в этот ответ.

Ответ 3

HttpClient 4.4 страдает от ошибки в этой области, связанной с проверкой возможно устаревших соединений перед возвратом запрашивающей стороне. Он не проверял, устарела ли связь, и это приводит к немедленному NoHttpResponseException.

Эта проблема была решена в HttpClient 4.4.1. Смотрите этот JIRA и заметки о выпуске

Ответ 4

В настоящее время большинство HTTP-соединений считаются постоянными, если не указано иное. Однако, чтобы сохранить ресурсы сервера, соединение редко остается открытым навсегда, время ожидания соединения по умолчанию для многих серверов довольно короткое, например, 5 секунд для Apache httpd 2.2 и выше.

Ошибка org.apache.http.NoHttpResponseException скорее всего, связана с одним постоянным соединением, которое было закрыто сервером.

Можно задать максимальное время для сохранения неиспользуемых соединений в пуле клиентов Apache Http в миллисекундах.

С Spring Boot, один из способов добиться этого:

public class RestTemplateCustomizers {
    static public class MaxConnectionTimeCustomizer implements RestTemplateCustomizer {

        @Override
        public void customize(RestTemplate restTemplate) {
            HttpClient httpClient = HttpClientBuilder
                .create()
                .setConnectionTimeToLive(1000, TimeUnit.MILLISECONDS)
                .build();

            restTemplate.setRequestFactory(
                new HttpComponentsClientHttpRequestFactory(httpClient));
        }
    }
}

// In your service that uses a RestTemplate
public MyRestService(RestTemplateBuilder builder ) {
    restTemplate = builder
         .customizers(new RestTemplateCustomizers.MaxConnectionTimeCustomizer())
         .build();
}

Ответ 5

Это может произойти, если disableContentCompression() установлен в диспетчере пула, назначенном вашему HttpClient, и целевой сервер пытается использовать сжатие gzip.

Ответ 6

Та же проблема для меня на apache http клиенте 4.5.5 с добавлением заголовка по умолчанию

Подключение: закрыть

решить проблему

Ответ 7

Хотя принятый ответ правильный, но ИМХО это просто обходной путь.

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

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

Почему?

  1. OkHttp объединяет http-соединения по умолчанию.
  2. Он изящно восстанавливается из ситуаций, когда http-соединение становится устаревшим и запрос не может быть повторен из-за неидемпотентности (например, POST). Я не могу сказать это о Apache HttpClient (упоминается NoHttpResponseException).
  3. Поддерживает HTTP/2.0 из ранних версий и бета-версий.

Когда я переключился на OkHttp, мои проблемы с NoHttpResponseException исчезли навсегда.

Ответ 8

Я столкнулся с той же проблемой, но решил ее, добавив "connection: close" как расширение,

Шаг 1: создайте новый класс ConnectionCloseExtension

import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.Parameters;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.HttpHeader;
import com.github.tomakehurst.wiremock.http.HttpHeaders;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.Response;

public class ConnectionCloseExtension extends ResponseTransformer {
  @Override
  public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
    return Response.Builder
        .like(response)
        .headers(HttpHeaders.copyOf(response.getHeaders())
            .plus(new HttpHeader("Connection", "Close")))
        .build();
  }

  @Override
  public String getName() {
    return "ConnectionCloseExtension";
  }
}

Шаг 2: установите класс расширения в wireMockServer, как показано ниже,

final WireMockServer wireMockServer = new WireMockServer(options()
                .extensions(ConnectionCloseExtension.class)
                .port(httpPort));