Я интегрируюсь с обработчиком платежей и пытаюсь справиться со сценарием, в котором:
Поэтому мне нужно запустить 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
при доступе из внутреннего класса. Есть ли более элегантный способ сделать это?
Использование 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();
никогда не будет выполнен...
Хорошая точка зрения. Если вы напрямую return
отправляете сообщение (в отличие от записи в поток ответов), самым простым способом было бы сделать последующее асинхронным. Ожидание get()
и последующая отправка предупреждения о превышении порога могут произойти, когда они будут готовы. Ответ был обновлен в этих строках. Другая возможность — перепроверить, был ли ответ отправлен обратно в первом блоке после builder.post()
. Это более эффективно для подсчета потоков в случае сбоя, но для связи между двумя потоками потребуется немного больше общего состояния и/или синхронизации.
Можно ли завершить логику кода, который вы предоставляете через CompletableFuture
в jdk, что я пробовал, но не смог найти способ сделать
ИМО, это классический вариант использования, который должен быть реализован как реактивный сервис. Возможно, вас заинтересует что-то похожее на blog.payara.fish/…