Прервать, если вызов API к платежному процессору занимает более 60 секунд

Я интегрируюсь с обработчиком платежей и пытаюсь справиться со сценарием, в котором:

  • пользователь нажимает оплатить и делается запрос на наш сервер
  • наш сервер делает запрос к платежной системе
  • на стороне обработчика платежей наблюдается значительная задержка
  • после определенного порога, например. 60 секунд, мы предупреждаем пользователя, что его платеж не прошел
  • через 70 секунд платежный процессор возвращает успешный ответ.

Поэтому мне нужно запустить API-вызов обработчика платежей из HTTP-вызова из пользовательского интерфейса, затем, если это займет более 60 секунд, завершить HTTP-вызов и вернуть пользователю ошибку, затем, если вызов API к платежу Процессор в конечном итоге преуспевает (скажем, через 70 секунд), отправьте электронное письмо команде администраторов.

Я думаю о чем-то вроде этого:

    import javax.ws.rs.client.*;
    import java.util.Timer;
    import java.util.TimerTask;

    ...

    boolean overThreshold = false;
    int timeout = 60; // seconds
    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            overThreshold = true;
            // return a message to user here saying their payment could not be processed
        }
    };

    new Timer(true).schedule(task, timeout * 1000);

    Client client = ClientBuilder.newClient();
    WebTarget webTarget
            = client.target({url of payment processor});
    Invocation.Builder builder = webTarget.request()
            .header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON);

    final Response response = builder.post(Entity.json(new Gson().toJson(request)));

    if (overThreshold) {
        // send alert email here
    }

Есть несколько проблем, например. метод run() имеет возвращаемое значение void, ошибка с overThreshold при доступе из внутреннего класса. Есть ли более элегантный способ сделать это?

ИМО, это классический вариант использования, который должен быть реализован как реактивный сервис. Возможно, вас заинтересует что-то похожее на blog.payara.fish/…

Prateek Jain 15.04.2019 13:54
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
1
209
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Использование Future.get(время ожидания) из ИсполнительСервис должно справиться с этим довольно четко.

Например:

    ExecutorService executor = Executors.newCachedThreadPool();

    // ... set up builder as before ...
    Future<Response> responseFuture = executor.submit(
            () -> builder.post(Entity.json(new Gson().toJson(request))));
    try {
        Response response = responseFuture.get(timeout, TimeUnit.SECONDS);
        // return normal response here
    } catch (TimeoutException ex) {
        executor.submit( () -> {
            Response lateResponse = responseFuture.get();
            // send overThreshold alert email here
            // Dummy return - prefer Callable to Runnable here for exception handling
            return null;
        } );
        // return a message to user here saying their payment could not be processed
    }

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

На самом деле, просто реализую это - если я верну сообщение пользователю, то response = responseFuture.get(); никогда не будет выполнен...

Mark 15.04.2019 12:02

Хорошая точка зрения. Если вы напрямую returnотправляете сообщение (в отличие от записи в поток ответов), самым простым способом было бы сделать последующее асинхронным. Ожидание get() и последующая отправка предупреждения о превышении порога могут произойти, когда они будут готовы. Ответ был обновлен в этих строках. Другая возможность — перепроверить, был ли ответ отправлен обратно в первом блоке после builder.post(). Это более эффективно для подсчета потоков в случае сбоя, но для связи между двумя потоками потребуется немного больше общего состояния и/или синхронизации.

df778899 15.04.2019 13:33

Можно ли завершить логику кода, который вы предоставляете через CompletableFuture в jdk, что я пробовал, но не смог найти способ сделать

zysaaa 19.11.2019 12:28

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