Spring Webflux timeout с несколькими клиентами - PullRequest
0 голосов
/ 08 мая 2019

У меня есть сервис, который взаимодействует с парой других сервисов.Поэтому я создал для них отдельные веб-клиенты (из-за разных базовых путей).Я установил тайм-ауты для них индивидуально на основе https://docs.spring.io/spring/docs/5.1.6.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-builder-reactor-timeout, но это, похоже, не работает эффективно.Для одной из служб попытался уменьшить время чтения до 2 секунд, но служба, по-видимому, не отключилась (журналы, использующие logging.level.org.springframework.web.reactive=debug, показывают, что выполнение запроса занимает около 6-7 секунд).

Я использую spring5.1 и netty 0.8, но я использую блокировку с веб-клиентом, потому что мы еще не пошли в олл-ин с webflux.Я попытался немного поиграть с таймаутами для каждого из вызовов, и кажется, что некоторые вызовы реагируют на тайм-аут, а другие - нет (более подробно см. Код ниже)

Как инициализировать веб-клиентов -

@Bean
public WebClient serviceAWebClient(@Value("${serviceA.basepath}") String basePath,
                                          @Value("${serviceA.connection.timeout}") int connectionTimeout,
                                          @Value("${serviceA.read.timeout}") int readTimeout,
                                          @Value("${serviceA.write.timeout}") int writeTimeout) {

    return getWebClientWithTimeout(basePath, connectionTimeout, readTimeout, writeTimeout);
}

@Bean
public WebClient serviceBWebClient(@Value("${serviceB.basepath}") String basePath,
                                           @Value("${serviceB.connection.timeout}") int connectionTimeout,
                                           @Value("${serviceB.read.timeout}") int readTimeout,
                                           @Value("${serviceB.write.timeout}") int writeTimeout) {

    return getWebClientWithTimeout(basePath, connectionTimeout, readTimeout, writeTimeout);
}

@Bean
public WebClient serviceCWebClient(@Value("${serviceC.basepath}") String basePath,
                                           @Value("${serviceC.connection.timeout}") int connectionTimeout,
                                           @Value("${serviceC.read.timeout}") int readTimeout,
                                           @Value("${serviceC.write.timeout}") int writeTimeout) {

    return getWebClientWithTimeout(basePath, connectionTimeout, readTimeout, writeTimeout);
}

private WebClient getWebClientWithTimeout(String basePath,
                                          int connectionTimeout,
                                          int readTimeout,
                                          int writeTimeout) {


    TcpClient tcpClient = TcpClient.create()
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout)
            .doOnConnected(connection ->
                    connection.addHandlerLast(new ReadTimeoutHandler(readTimeout))
                            .addHandlerLast(new WriteTimeoutHandler(writeTimeout)));

    return WebClient.builder().baseUrl(basePath)
            .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient))).build();

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

Mono<ResponseA> serviceACallMono = ..;
Mono<ResponseB> serviceBCallMono = ..;
Mono.zip(serviceACallMono,serviceBCallMono,
(serviceAResponse, serviceBResponse) -> serviceC.getImportantData(serviceAResponse,serviceBResponse))
.flatMap(Function.identity)
.block();

Итак, в приведенном выше замечании я заметил следующее -

Если опуститьserviceA ReadTimeout, я получаю ошибку тайм-аута.

Если я опускаю serviceB ReadTimeout, я получаю ошибку тайм-аута.

Если я понижаю serviceC ReadTimeout, он НЕ реагирует на понижениеReadTimeout.Он просто продолжает работать, пока не получит ответ.

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

Редактировать: Обновить, чтобы я мог воспроизвести проблему более простым способом.Так, для чего-то вроде -

return serviceACallMono
                .flatMap(notUsed -> serviceBCallMono);

Тайм-аут serviceACallMono соблюдается, но независимо от того, на сколько вы его понизите для serviceB, он не истекает.

И если вы просто перевернетеorder -

return serviceBCallMono
                .flatMap(notUsed -> serviceACallMono);

Теперь тайм-аут для serviceB соблюден, а для serviceA - нет.

Я обновил службу, чтобы она также возвращала Mono, наблюдая за поведением в этом Edit.

Edit 2: Это, по сути, то, что происходит в ServiceC # getImportantData -

@Override
    public Mono<ServiceCResponse> getImportantData(ServiceAResponse requestA,
                                                   ServiceBResponse requestB) {

        return serviceCWebClient.post()
                .uri(GET_IMPORTANT_DATA_PATH, requestB.getAccountId())
                .body(BodyInserters.fromObject(formRequest(requestA)))
                .retrieve()
                .bodyToMono(ServiceC.class);
    }

formRequest - это простой метод преобразования POJO.

1 Ответ

0 голосов
/ 18 мая 2019

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

...