С самого начала я всегда путался с тем, как бороться с InterruptedException и как правильно отменить HTTP-запрос, если они занимают слишком много времени. У меня есть библиотека, в которой я предоставил два метода, синхронизацию и асинхронность для нашего клиента. Они могут называть тот метод, который, по их мнению, подходит для их целей.
- executeSync() - ждет, пока у меня не будет результата, вернет результат.
- executeAsync() - немедленно возвращает Будущее, которое может быть обработано после выполнения других действий, если это необходимо.
Они передадут объект DataKey
, в котором есть идентификатор пользователя и время ожидания. Мы выясним, какую машину вызывать на основе идентификатора пользователя, а затем создать URL-адрес с этой машиной, и мы сделаем http-обращение к URL-адресу с помощью AsyncRestTemplate, а затем отправьте ответ им на вопрос, успешно ли это или нет.
Я использую exchange метод AsyncRestTemplate
, который возвращает обратно ListenableFuture
, и я хотел иметь асинхронную неблокирующую архитектуру с подключением к NIO-подключению, поэтому этот запрос использует неблокирующий IO, поэтому я пошел с AsyncRestTemplate
. Подходит ли этот подход к определению моей проблемы? Эта библиотека будет использоваться в производстве с очень большой нагрузкой.
Ниже мой интерфейс:
public interface Client {
// for synchronous
public DataResponse executeSync(DataKey key);
// for asynchronous
public ListenableFuture<DataResponse> executeAsync(DataKey key);
}
И ниже моя реализация интерфейса:
public class DataClient implements Client {
// using spring 4 AsyncRestTemplate
private final AsyncRestTemplate restTemplate = new AsyncRestTemplate();
// for synchronous
@Override
public DataResponse executeSync(DataKey keys) {
Future<DataResponse> responseFuture = executeAsync(keys);
DataResponse response = null;
try {
response = responseFuture.get(keys.getTimeout(), TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
// do we need to catch InterruptedException here and interrupt the thread?
Thread.currentThread().interrupt();
// also do I need throw this RuntimeException at all?
throw new RuntimeException("Interrupted", ex);
} catch (TimeoutException ex) {
DataLogging.logEvents(ex, DataErrorEnum.CLIENT_TIMEOUT, keys);
response = new DataResponse(null, DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
responseFuture.cancel(true); // terminating the tasks that got timed out so that they don't take up the resources?
} catch (Exception ex) {
DataLogging.logEvents(ex, DataErrorEnum.ERROR_CLIENT, keys);
response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR);
}
return response;
}
// for asynchronous
@Override
public ListenableFuture<DataResponse> executeAsync(final DataKey keys) {
final SettableFuture<DataResponse> responseFuture = SettableFuture.create();
final org.springframework.util.concurrent.ListenableFuture orig =
restTemplate.exchange(createURL(keys), HttpMethod.GET, keys.getEntity(), String.class);
orig.addCallback(
new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onSuccess(ResponseEntity<String> result) {
responseFuture.set(new DataResponse(result.getBody(), DataErrorEnum.OK,
DataStatusEnum.SUCCESS));
}
@Override
public void onFailure(Throwable ex) {
DataLogging.logErrors(ex, DataErrorEnum.ERROR_SERVER, keys);
responseFuture.set(new DataResponse(null, DataErrorEnum.ERROR_SERVER,
DataStatusEnum.ERROR));
}
});
// propagate cancellation back to the original request
responseFuture.addListener(new Runnable() {
@Override public void run() {
if (responseFuture.isCancelled()) {
orig.cancel(false); // I am keeping this false for now
}
}
}, MoreExecutors.directExecutor());
return responseFuture;
}
}
И клиент будет так звонить по своему коду -
// if they are calling executeSync() method
DataResponse response = DataClientFactory.getInstance().executeSync(dataKey);
// and if they want to call executeAsync() method
Future<DataResponse> response = DataClientFactory.getInstance().executeAsync(dataKey);
Теперь вопрос -
-
Можем ли мы прерывать вызов
AsyncRestTemplate
, если HTTP-запрос занимает слишком много времени? Я на самом деле вызываюcancel
на моемfuture
в моем выше коде в методеexecuteSync
, но я не уверен, как проверить его, чтобы убедиться, что он делает то, что должен? Я хочу отменить отмену в исходное будущее, чтобы отменить соответствующий HTTP-запрос (который я, вероятно, хочу сделать для сохранения ресурсов), поэтому я добавил слушателя в свой метод executeAsync. Я считаю, мы не можем прерывать вызовыRestTemplate
, но не уверены вAsyncRestTemplate
, можем ли мы это сделать или нет. Если допустим, что мы можем прерывать вызовыAsyncRestTemplate
, тогда я делаю все правильно, чтобы прервать http-вызовы? Или есть лучший/более чистый способ сделать это? Или мне даже нужно беспокоиться об отмене запроса Http с помощьюAsyncRestTemplate
с моим текущим дизайном?// propagate cancellation back to the original request responseFuture.addListener(new Runnable() { @Override public void run() { if (responseFuture.isCancelled()) { orig.cancel(false); // I am keeping this false for now } } }, MoreExecutors.directExecutor());
С текущей настройкой, я вижу, что это иногда исключает CancellationException (не каждый раз). Означает ли это, что мой HTTP-запрос был отменен?
- Также я делаю правильную вещь в блоке catch
InterruptedException
вexecuteSync
методе? Если нет, то какой правильный способ справиться с этим. И мне вообще нужно иметь дело сInterruptedException
в моем случае? - Правда ли, что по умолчанию
AsyncRestTamplete
использует блокирующие вызовы и запросы на поток? Если да, то есть ли способ подключения клиентских подключений на основе NIO в моей текущей настройке?
Любые объяснения/предложения кода будут очень полезны.