Micronaut ReadTimeoutException

У меня есть приложение Grails 4, предоставляющее REST API. Одна из конечных точек иногда выходит из строя за следующим исключением:

io.micronaut.http.client.exceptions.ReadTimeoutException: Read Timeout
    at io.micronaut.http.client.exceptions.ReadTimeoutException.<clinit>(ReadTimeoutException.java:26)
    at io.micronaut.http.client.DefaultHttpClient$10.exceptionCaught(DefaultHttpClient.java:1917)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:297)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:276)
    at io.netty.channel.AbstractChannelHandlerContext.fireExceptionCaught(AbstractChannelHandlerContext.java:268)
    at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireExceptionCaught(CombinedChannelDuplexHandler.java:426)
    at io.netty.channel.ChannelHandlerAdapter.exceptionCaught(ChannelHandlerAdapter.java:92)
    at io.netty.channel.CombinedChannelDuplexHandler$1.fireExceptionCaught(CombinedChannelDuplexHandler.java:147)
    at io.netty.channel.ChannelInboundHandlerAdapter.exceptionCaught(ChannelInboundHandlerAdapter.java:143)
    at io.netty.channel.CombinedChannelDuplexHandler.exceptionCaught(CombinedChannelDuplexHandler.java:233)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:297)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:276)
    at io.netty.channel.AbstractChannelHandlerContext.fireExceptionCaught(AbstractChannelHandlerContext.java:268)
    at io.netty.handler.timeout.ReadTimeoutHandler.readTimedOut(ReadTimeoutHandler.java:98)
    at io.netty.handler.timeout.ReadTimeoutHandler.channelIdle(ReadTimeoutHandler.java:90)
    at io.netty.handler.timeout.IdleStateHandler$ReaderIdleTimeoutTask.run(IdleStateHandler.java:505)
    at io.netty.handler.timeout.IdleStateHandler$AbstractIdleTask.run(IdleStateHandler.java:477)
    at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
    at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:127)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:405)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:834)

Конечная точка использует http-клиент micronaut для вызова других систем. Удаленная система очень долго отвечает, что вызывает исключение ReadTimeOutException.

Вот код, вызывающий удаленную службу:

class RemoteTaskService implements GrailsConfigurationAware {

    String taskStepperUrl

    // initializes fields from configuration
    void setConfiguration(Config config) {
        taskStepperUrl = config.getProperty('services.stepper')
    }

    private BlockingHttpClient getTaskClient() {
        HttpClient.create(taskStepperUrl.toURL()).toBlocking()
    }

    List<Map> loadTasksByProject(long projectId) {
        try {
            retrieveRemoteList("/api/tasks?projectId=${projectId}")
        } catch(HttpClientResponseException e) {
            log.error("Loading tasks of project failed with status: ${e.status.code}: ${e.message}")
            throw new NotFoundException("No tasks found for project ${projectId}")
        }
    }

    private List<Map> retrieveRemoteList(String path) {
        HttpRequest request = HttpRequest.GET(path)
        HttpResponse<List> response = taskClient.exchange(request, List) as HttpResponse<List>
        response.body()
    }
}

Я попытался решить эту проблему, используя следующую конфигурацию в моем application.yml:

micronaut:
    server:
        read-timeout: 30

а также

micronaut.http.client.read-timeout: 30

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

Как изменить время ожидания чтения для клиента http rest?


person Heschoon    schedule 10.02.2020    source источник
comment
Непонятно, что вы используете для инициирования вызова REST. Вы можете показать этот код?   -  person Jeff Scott Brown    schedule 10.02.2020
comment
@JeffScottBrown В коде обнаружено использование клиента отдыха, вызывающего удаленный микросервис, на ответ которого требуется очень много времени. Код вызова удаленной службы был опубликован. Поскольку теперь я знаю, откуда исходит исключение, вопрос теперь в том, как изменить время ожидания чтения для моего клиента.   -  person Heschoon    schedule 10.02.2020


Ответы (2)


micronaut.http.client.read-timeout занимает время, поэтому вы должны добавить к значению единицу измерения, например 30s, 30m или 30h.

person jayvee    schedule 01.04.2021

Кажется, что значения конфигурации не вводятся в вручную созданных http-клиентах.

Решение - настроить HttpClient при создании, установив продолжительность readTimeout:

private BlockingHttpClient getTaskClient() {
    HttpClientConfiguration configuration = new DefaultHttpClientConfiguration()
    configuration.readTimeout = Duration.ofSeconds(30)
    new DefaultHttpClient(taskStepperUrl.toURL(), configuration).toBlocking()
}
person Heschoon    schedule 11.02.2020
comment
Я предполагаю, что конфигурация клиента по умолчанию (та, которую вы определяете с помощью micronaut.http.client), тоже может быть введена. - person cfrick; 11.02.2020
comment
Да, используя объект Config, я мог бы ввести его так же, как и для 'taskStepperUrl' - person Heschoon; 11.02.2020