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

Spring Тайм-аут RestTemplate

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

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

<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: Ошибка ввода-вывода: нет пути к хосту: connect; Вложенное исключение - это java.net.NoRouteToHostException: нет пути к хосту: connect

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

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

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

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

4b9b3361

Ответ 1

Наконец-то я получил эту работу.

Я думаю, что тот факт, что наш проект имел две разные версии банды 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 в своем коде и получаю значения таймаута из файла свойств.

Ответ 2

Для весенней загрузки> = 1.4

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

Для весенней загрузки <= 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 без Spring Boot, используя @Configuration вместо XML

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

        return new RestTemplate(httpRequestFactory);
    }
}

Ответ 3

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

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

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

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

Ответ 4

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

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

Ответ 5

Вот мои 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 чтобы установить соединение и время 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. Перегруженные методы, которые занимают непосредственно миллисекунды, теперь устарели.

Edit Испытано с Spring ботинке 2.1.0 и Java 11.

Ответ 6

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

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