Правильный способ добавить задержку ответа на запрос в настраиваемый HttpService

Вот моя текущая реализация HttpService.serve()

@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
    return HttpResponse.from(req.aggregate().thenApply(ahr -> {
        MyResponse myResponse = Utils.handle(ahr);
        HttpResponse httpResponse Utils.toResponse(myResponse);
        return httpResponse; 
    }));
}

У меня есть определяемая пользователем задержка ответа, которая может варьироваться для каждого отдельного запроса-ответа, и это доступно в myResponse объект.

Как лучше всего применить эту задержку неблокирующим способом, я вижу некоторые delay API, но они защищены внутри HttpResponse. Любые дополнительные советы или указатели по дизайну потокового API или декораторам были бы полезны. Я действительно многому научился из кодовой базы Armeria:)

1 ответ

Решение

Если вы знаете желаемую задержку еще до использования тела запроса, вы можете просто использовать HttpResponse.delayed():

@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
    return HttpResponse.delayed(
        HttpResponse.of(200),
        Duration.ofSeconds(3),
        ctx.eventLoop());
}

Если вам нужно использовать контент или выполнить какую-либо операцию для расчета желаемой задержки, вы можете объединить HttpResponse.delayed() с участием HttpResponse.from():

@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
    return HttpResponse.from(req.aggregate().thenApply(ahr -> {
    //                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        MyResponse myResponse = Utils.handle(ahr);
        HttpResponse httpResponse = Utils.toResponse(myResponse);
        Duration myDelay = Utils.delayMillis(...);
        return HttpResponse.delayed(httpResponse, myDelay, ctx.eventLoop());
        //                  ^^^^^^^
    });
}

Если задержка на самом деле не задержка, а ожидание чего-то, вы можете использовать CompletableFuture.thenCompose() который более мощный:

@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
    return HttpResponse.from(req.aggregate().thenCompose(ahr -> {
    //                                       ^^^^^^^^^^^
        My1stResponse my1stRes = Utils.handle(ahr);

        // Schedule some asynchronous task that returns another future.
        CompletableFuture<My2ndResponse> myFuture = doSomething(my1stRes);

        // Map the future into an HttpResponse.
        return myFuture.thenApply(my2ndRes -> {
            HttpResponse httpRes = Utils.toResponse(my1stRes, my2ndRes);
            return httpRes;
        });
    });
}

Для еще более сложного рабочего процесса я бы рекомендовал вам изучить реализации Reactive Streams, такие как Project Reactor и RxJava, которые предоставляют инструменты, позволяющие избежать ада обратных вызовов.

Другие вопросы по тегам