[英]Multipart request with retrofit 2.0 Android Taking too much time to request
[英]How to cancel AsyncRestTemplate HTTP request if they are taking too much time?
從開始以來,我總是混淆如何處理InterruptedException以及如果他們花費太多時間如何正確取消http請求。 我有一個庫,我在其中為客戶提供了兩種方法,sync和async。 他們可以調用他們認為適合他們目的的任何方法。
它們將傳遞具有用戶ID和超時值的DataKey
對象。 我們將根據用戶ID確定調用哪台計算機,然后使用該計算機創建一個URL,我們將使用AsyncRestTemplate對URL進行http調用,然后根據它是否成功將響應發送給它們。
我正在使用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);
現在的問題是 -
如果http請求耗時太長,我們可以中斷AsyncRestTemplate
調用嗎? 我實際上在executeSync
方法的上面的代碼中調用cancel
我的future
,但我不知道如何驗證它以確保它正在做它應該做的事情? 我想將取消傳播回原來的未來,以便我可以取消相應的http請求(我可能想要節省資源),這就是為什么我在executeAsync方法中添加了一個監聽器。 我相信,我們不能中斷RestTemplate
調用但不確定AsyncRestTemplate
是否可以這樣做。 如果讓我們說我們可以中斷AsyncRestTemplate
調用,那么我是否正在做一切正確的中斷http調用? 或者有更好/更清潔的方法嗎? 或者我是否需要擔心使用我當前的設計使用AsyncRestTemplate
取消Http請求?
// 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請求被取消了?
executeSync
方法中使用InterruptedException
catch塊做正確的事情嗎? 如果沒有,那么處理這個問題的正確方法是什么。 在我的情況下,我是否需要處理InterruptedException
? AsyncRestTamplete
使用阻塞調用和每個線程請求? 如果是,那么在我當前的設置中是否有任何方法可以使用基於NIO的客戶端連接? 任何解釋/代碼建議都會有很大幫助。
首先,您為什么使用SettableFuture? 為什么不能只返回AsyncRestTemplate返回的ListenableFuture?
1. Can we interrupt AsyncRestTemplate call if http request is taking too long?
你當然可以! 您只需要調用Future.cancel
方法。 此方法將中斷AsyncRestTemplate實際使用的內部RestTemplate的執行。
2. Also am I doing the right thing in catch block of InterruptedException in executeSync method?
正如Phil和Danilo所說,你不需要在InterruptedException catch塊中中斷當前線程。 當必須取消執行請求時,只需執行您需要執行的操作。
實際上,我建議您創建一個處理此行為的方法,例如handleInterruption,並將此方法用於TimeoutException
和InterruptedException
。
3. Is it true that by default AsyncRestTamplete uses blocking calls and request per thread?
是。 AsyncRestTamplete
的默認構造函數在內部使用SimpleClientHttpRequestFactory
和SimpleAsyncTaskExecutor
。
此TaskExecutor始終為每個任務啟動威脅,並且永遠不會重用Threads,因此效率非常低:
* TaskExecutor implementation that fires up a new Thread for each task,
* executing it asynchronously.
*
* Supports limiting concurrent threads through the "concurrencyLimit"
* bean property. By default, the number of concurrent threads is unlimited.
*
* NOTE: This implementation does not reuse threads! Consider a
* thread-pooling TaskExecutor implementation instead, in particular for
* executing a large number of short-lived tasks.
*
我建議你使用另一種AsyncRestTemplate配置。
您應該使用AsyncRestTemplate的構造函數,該構造函數使用另一個TaskExecutor:
public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor)
例如:
AsyncRestTemplate template = new AsyncRestTemplate(new ConcurrentTaskExecutor(Executors.newCachedThreadPool()));
此ExecutorService(Executors.newCachedThreadPool())根據需要創建新線程,但在可用時將重用先前構造的線程。
或者甚至更好,您可以使用另一個RequestFactory。 例如,您可以使用內部使用NIO的 HttpComponentsAsyncClientHttpRequestFactory
,只需調用AsyncRestTemplate的正確構造函數:
new AsyncRestTemplate(new HttpComponentsAsyncClientHttpRequestFactory())
不要忘記AsyncRestTemplate的內部行為將取決於您如何創建對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.