簡體   English   中英

如果占用太多時間,如何取消AsyncRestTemplate HTTP請求?

[英]How to cancel AsyncRestTemplate HTTP request if they are taking too much time?

從開始以來,我總是混淆如何處理InterruptedException以及如果他們花費太多時間如何正確取消http請求。 我有一個庫,我在其中為客戶提供了兩種方法,sync和async。 他們可以調用他們認為適合他們目的的任何方法。

  • executeSync() - 等到我有結果,返回結果。
  • executeAsync() - 立即返回一個Future,如果需要,可以在其他事情完成后處理。

它們將傳遞具有用戶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);

現在的問題是 -

  1. 如果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請求被取消了?

  2. 我也是在executeSync方法中使用InterruptedException catch塊做正確的事情嗎? 如果沒有,那么處理這個問題的正確方法是什么。 在我的情況下,我是否需要處理InterruptedException
  3. 默認情況下, 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,並將此方法用於TimeoutExceptionInterruptedException

3. Is it true that by default AsyncRestTamplete uses blocking calls and request per thread?

是。 AsyncRestTamplete的默認構造函數在內部使用SimpleClientHttpRequestFactorySimpleAsyncTaskExecutor

此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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM