Прервать HTTP-запрос через заданное количество секунд

Я использую Java 1.8, dropwizard 1.3.5 и swagger Inflection 1.0.13 для своего API.

У меня есть метод, который принимает HTTP-запрос, задерживает 20 секунд, а затем возвращает ответ с кодом состояния 200:

public ResponseContext delayBy20Seconds(RequestContext context) {
    ResponseContext response = new ResponseContext().contentType(MediaType.APPLICATION_JSON_TYPE);

    Thread.sleep(20000);

    response.status(Response.Status.OK);
    return response;
}

Скажем, я хочу вернуть код состояния 400, если операция (которая в данном случае занимает 20 секунд) занимает более 15 секунд. Как бы я этого добился?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
0
197
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете использовать что-то вроде TimeLimiter из библиотеки Google Guava. Это позволяет вам обернуть вызываемый объект в операцию, которую вы можете вызвать с помощью Timeout. Если вызываемый объект не завершит операцию вовремя, он выдаст TimeoutException, который вы можете поймать и вернуть ответ 400.

Например:

TimeLimiter timeLimiter = new SimpleTimeLimiter();
try {
  String result = timeLimiter.callWithTimeout(
                () -> doSomeHeavyWeightOperation(), 15, TimeUnit.SECONDS);
} catch (TimeoutException e) {
  // return 400
}
Ответ принят как подходящий

Один из способов сделать это без дополнительных библиотек — использовать пакет java.util.concurrent. Самый надежный способ отменить такую ​​длительную задачу — запустить ее в отдельном потоке.

import java.util.concurrent.*;

...

private ExecutorService exec = Executors.newSingleThreadExecutor();
public ResponseContext delayBy20Seconds(RequestContext context) {
    Callable<ResponseContext> task = new Callable<ResponseContext>() {
        @Override
        public ResponseContext call() throws Exception {
            Thread.sleep(20000);
            return new ResponseContext().contentType(MediaType.APPLICATION_JSON_TYPE);
        }
    };
    List<Callable<ResponseContext>> tasks = new ArrayList<>();
    tasks.add(task);
    List<Future<ResponseContext>> done = exec.invokeAll(tasks, 15, TimeUnit.SECONDS);
    Future<ResponseContext> task1 = done.get(0);
    if (task1.isCancelled()) {
        return some Error Response;
    }
    return task1.get();
}

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

Реализация Callable<ResponseContext> — это место, где выполняется работа над долговременной задачей. И, как должно быть очевидно, в вызове exec.invokeAll мы сообщаем ему, сколько мы готовы ждать. Список возвращаемых фьючерсов всегда будет содержать столько же элементов, сколько и список задач, поэтому нет необходимости проверять его на пустоту. Нам просто нужно проверить, выполнена ли задача или нет.

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