Время ожидания Spring RestTemplate


125

Я хотел бы установить тайм-ауты подключения для службы отдыха, используемой моим веб-приложением. Я использую Spring RestTemplate, чтобы общаться со своим сервисом. Я провел некоторое исследование, и я нашел и использовал xml ниже (в моем приложении xml), который, как мне кажется, предназначен для установки тайм-аута. Я использую Spring 3.0.

Я также видел ту же проблему здесь Конфигурация тайм-аута для веб-служб Spring с RestTemplate, но решения не кажутся такими чистыми , я бы предпочел установить значения тайм-аута через конфигурацию Spring

<bean id="RestOperations" class="org.springframework.web.client.RestTemplate">
    <constructor-arg>

      <bean class="org.springframework.http.client.CommonsClientHttpRequestFactory">
        <property name="readTimeout" value="${restURL.connectionTimeout}" />
      </bean>
    </constructor-arg>
</bean>

Кажется, что бы я ни установил readTimeout, я получаю следующее:

Сетевой кабель отключен: ожидает около 20 секунд и сообщает следующее исключение:

org.springframework.web.client.ResourceAccessException: Ошибка ввода-вывода: нет маршрута к хосту: подключиться; вложенное исключение - java.net.NoRouteToHostException: нет маршрута к хосту: подключиться

Неверный URL-адрес, поэтому служба отдыха возвращает 404: ожидает около 10 секунд и сообщает следующее исключение:

org.springframework.web.client.HttpClientErrorException: 404 не найдено

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

Большое спасибо.

Ответы:


164

Для Spring Boot> = 1,4

@Configuration
public class AppConfig
{
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) 
    {
        return restTemplateBuilder
           .setConnectTimeout(...)
           .setReadTimeout(...)
           .build();
    }
}

Для Spring Boot <= 1.3

@Configuration
public class AppConfig
{
    @Bean
    @ConfigurationProperties(prefix = "custom.rest.connection")
    public HttpComponentsClientHttpRequestFactory customHttpRequestFactory() 
    {
        return new HttpComponentsClientHttpRequestFactory();
    }

    @Bean
    public RestTemplate customRestTemplate()
    {
        return new RestTemplate(customHttpRequestFactory());
    }
}

тогда в вашем application.properties

custom.rest.connection.connection-request-timeout=...
custom.rest.connection.connect-timeout=...
custom.rest.connection.read-timeout=...

Это работает , потому что HttpComponentsClientHttpRequestFactoryесть государственные сеттеры connectionRequestTimeout, connectTimeoutи readTimeoutи @ConfigurationPropertiesустанавливает их для вас.


Для Spring 4.1 или Spring 5 без пружинного ботинка , используя @ConfigurationвместоXML

@Configuration
public class AppConfig
{
    @Bean
    public RestTemplate customRestTemplate()
    {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setConnectionRequestTimeout(...);
        httpRequestFactory.setConnectTimeout(...);
        httpRequestFactory.setReadTimeout(...);

        return new RestTemplate(httpRequestFactory);
    }
}

Хороший пример! Пожалуйста, удалите newSpring Boot
лишнее

7
Обратите внимание, что после этой настройки RestTemplate будет использовать http-клиент apache (для установки тайм-аута). Количество потоков maxPerRoute по умолчанию для пула подключений клиентов Apache http составляет 5, а максимальное общее количество потоков равно 10 (httpClient-4.5.2). Нам нужно установить это самостоятельно в некоторых ситуациях (например, нам нужно подключиться ко многим хостам и нам нужно больше подключений).
bluearrow

2
Обратите внимание: connectionRequestTimeoutатрибут недоступен до 4.1.4. РЕЛИЗ
Taoufik Mohdit

Я попробовал конфигурацию для Spring Boot> = 1.4 на Spring Boot> = 2.1.8, и у меня ничего не получилось. Я подписался на этот пост ( zetcode.com/springboot/resttemplate ), чтобы сделать эту конфигурацию.
Анджело Полотто

@ ÂngeloPolotto размещенная вами ссылка дает тот же совет, что и это решение. В статье говорится: «В качестве альтернативы мы можем использовать RestTemplateBuilder для выполнения этой работы».
dustin.schultz

76

Наконец-то я заработал.

Я думаю, что тот факт, что в нашем проекте были две разные версии jar-файла commons-httpclient, не помог. Разобравшись с этим, я обнаружил, что вы можете делать две вещи ...

В коде вы можете указать следующее:

HttpComponentsClientHttpRequestFactory rf =
    (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(1 * 1000);
rf.setConnectTimeout(1 * 1000);

При первом вызове этого кода он устанавливает тайм-аут для HttpComponentsClientHttpRequestFactoryкласса, используемого RestTemplate. Следовательно, все последующие вызовы, сделанныеRestTemplate будут использовать настройки тайм-аута, определенные выше.

Или лучший вариант - сделать это:

<bean id="RestOperations" class="org.springframework.web.client.RestTemplate">
    <constructor-arg>
        <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
            <property name="readTimeout" value="${application.urlReadTimeout}" />
            <property name="connectTimeout" value="${application.urlConnectionTimeout}" />
        </bean>
    </constructor-arg>
</bean>

Где я использую RestOperationsинтерфейс в своем коде и получаю значения тайм-аута из файла свойств.


Таким образом, это устанавливает тайм-ауты для всех вызовов через этот шаблон отдыха (который является одиночным). Знаете ли вы, можно ли контролировать тайм-ауты для каждого запроса? (например: 10 секунд для пост-звонка и 5 секунд для получения звонка и т. д.)
codealsa

@ sardo. Где я использую интерфейс RestOperations в своем коде. нам нужно создать для этого какой-то явный интерфейс?
deadend

Вы сказали, что используете Spring 3.0, с которым я тоже застрял, но в 3.0 нет HttpComponentsClientHttpRequestFactory! Вы обновляли Spring?
Kutzi 01

5
Приведенный выше код не работает в последней версии Spring. Это дает ClassCastExceptionjava.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
участник

41

Этот вопрос является первой ссылкой для поиска Spring Boot, поэтому было бы здорово разместить здесь решение, рекомендованное в официальной документации . Spring Boot имеет собственный удобный bean-компонент RestTemplateBuilder :

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

    return restTemplateBuilder
            .setConnectTimeout(Duration.ofSeconds(500))
            .setReadTimeout(Duration.ofSeconds(500))
            .build();
}

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


2
Примечание для новичков Spring, таких как я: просто вставка этого в @Configuration ничего не даст. Этот метод требует, чтобы вы внедрили этот RestTemplate в какое-то место, которое использует его в качестве аргумента конструктора RestTemplateXhrTransport, который вы, в свою очередь, добавите в свой список транспортов, который вы передаете своему SocksJSClient.
Key Lay

setConnectTimeoutи некоторые реализации setReadTimeoutустарели
скрывец

17

Вот мои 2 цента. Ничего нового, кроме некоторых пояснений, улучшений и нового кода.

По умолчанию RestTemplateимеет бесконечное время ожидания. Существует два вида тайм-аутов: тайм-аут соединения и тайм-аут чтения. Например, я мог подключиться к серверу, но не мог читать данные. Приложение зависало, и вы не понимаете, что происходит.

Я собираюсь использовать аннотации, которые в наши дни предпочтительнее XML.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

        factory.setConnectTimeout(3000);
        factory.setReadTimeout(3000);

        return new RestTemplate(factory);
    }
}

Здесь мы используем SimpleClientHttpRequestFactoryдля установки соединения и считывания таймаутов. Затем он передается конструктору RestTemplate.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

        return builder
                .setConnectTimeout(Duration.ofMillis(3000))
                .setReadTimeout(Duration.ofMillis(3000))
                .build();
    }
}

Во втором решении мы используем расширение RestTemplateBuilder. Также обратите внимание на параметры двух методов: они принимают Duration. Перегруженные методы, которые занимают миллисекунды, теперь не рекомендуются.

Редактировать Протестировано с помощью Spring Boot 2.1.0 и Java 11.


Какую версию Spring и Java вы используете?
орираб

2
Spring Boot 2.1.0 и Java 11. В качестве рабочего примера вы можете взглянуть на мой учебник: zetcode.com/springboot/resttemplate
Ян Боднар,

Предлагаю добавить это к ответу
orirab

См. Github.com/spring-projects/spring-boot/blob/master/… . Он был добавлен в Spring Boot 2.1.0.
Ян Боднар

Спасибо, @JanBodnar, ваше руководство - единственное, что хорошо сработало на моей Spring Boot 5.x
Анджело Полотто

15

Вот действительно простой способ установить тайм-аут:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
      new HttpComponentsClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(timeout);
    return clientHttpRequestFactory;
}

0

У меня был аналогичный сценарий, но также требовалось установить прокси. Самый простой способ, который я мог сделать для этого, - это расширить, SimpleClientHttpRequestFactoryчтобы упростить настройку прокси (разные прокси для непродовольственных и прокси-серверов). Это все равно должно работать, даже если вам не нужен прокси. Затем в моем расширенном классе я переопределяю openConnection(URL url, Proxy proxy)метод, используя то же, что и исходный , но просто устанавливаю таймауты перед возвратом.

@Override
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
    URLConnection urlConnection = proxy != null ? url.openConnection(proxy) : url.openConnection();
    Assert.isInstanceOf(HttpURLConnection.class, urlConnection);
    urlConnection.setConnectTimeout(5000);
    urlConnection.setReadTimeout(5000);
    return (HttpURLConnection) urlConnection;
}

0

Чтобы расширить ответ benscabbia :

private RestTemplate restCaller = new RestTemplate(getClientHttpRequestFactory());

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int connectionTimeout = 5000; // milliseconds
    int socketTimeout = 10000; // milliseconds
    RequestConfig config = RequestConfig.custom()
      .setConnectTimeout(connectionTimeout)
      .setConnectionRequestTimeout(connectionTimeout)
      .setSocketTimeout(socketTimeout)
      .build();
    CloseableHttpClient client = HttpClientBuilder
      .create()
      .setDefaultRequestConfig(config)
      .build();
    return new HttpComponentsClientHttpRequestFactory(client);
}

0
  1. Тайм-аут RestTemplate с SimpleClientHttpRequestFactory Чтобы программно переопределить свойства тайм-аута, мы можем настроить класс SimpleClientHttpRequestFactory, как показано ниже.

Переопределить тайм-аут с помощью SimpleClientHttpRequestFactory

//Create resttemplate
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

//Override timeouts in request factory
private SimpleClientHttpRequestFactory getClientHttpRequestFactory() 
{
    SimpleClientHttpRequestFactory clientHttpRequestFactory
                      = new SimpleClientHttpRequestFactory();
    //Connect timeout
    clientHttpRequestFactory.setConnectTimeout(10_000);

    //Read timeout
    clientHttpRequestFactory.setReadTimeout(10_000);
    return clientHttpRequestFactory;
}
  1. Тайм-аут RestTemplate с HttpComponentsClientHttpRequestFactory SimpleClientHttpRequestFactory помогает установить тайм-аут, но он очень ограничен по функциональности и может оказаться недостаточным в приложениях реального времени. В производственном коде мы можем использовать HttpComponentsClientHttpRequestFactory, который поддерживает клиентскую библиотеку HTTP вместе с resttemplate.

HTTPClient предоставляет другие полезные функции, такие как пул соединений, управление незанятыми соединениями и т. Д.

Подробнее: Пример конфигурации Spring RestTemplate + HttpClient

Переопределить тайм-аут с помощью HttpComponentsClientHttpRequestFactory

//Create resttemplate
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

//Override timeouts in request factory
private SimpleClientHttpRequestFactory getClientHttpRequestFactory() 
{
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
                      = new HttpComponentsClientHttpRequestFactory();
    //Connect timeout
    clientHttpRequestFactory.setConnectTimeout(10_000);

    //Read timeout
    clientHttpRequestFactory.setReadTimeout(10_000);
    return clientHttpRequestFactory;
}

ссылка: Пример конфигурации тайм-аута Spring RestTemplate

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.