복붙노트

[SPRING] AsyncRestTemplate HTTP 요청을 너무 오래 걸리는 경우이를 취소하는 방법?

SPRING

AsyncRestTemplate HTTP 요청을 너무 오래 걸리는 경우이를 취소하는 방법?

InterruptedException을 처리하는 방법과 너무 많은 시간이 걸리는 경우 http 요청을 올바르게 취소하는 방법을 항상 혼동했습니다. 나는 고객에게 동기화와 비동기라는 두 가지 방법을 제공 한 라이브러리를 가지고있다. 그들은 자신의 목적에 맞는 방법으로 전화를 걸 수 있습니다.

사용자 ID와 타임 아웃 값을 가진 DataKey 객체를 전달합니다. 사용자 ID를 기반으로 호출 할 시스템을 파악한 다음 해당 시스템에서 URL을 작성하면 AsyncRestTemplate을 사용하여 http로 URL을 호출 한 다음 성공했는지 여부에 따라 응답을 다시 전송합니다.

내가 ListenableFuture 다시 반환 AsyncRestTemplate 교환 메서드를 사용하고 비동기 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. ==============================

    1.무엇보다도 SettableFuture를 사용하는 이유는 무엇입니까? AsyncRestTemplate에 의해 반환 된 ListenableFuture를 반환 할 수없는 이유는 무엇입니까?

    무엇보다도 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의 다른 구성을 사용하는 것이 좋습니다.

    다른 TaskExecutor를 사용하는 AsyncRestTemplate의 생성자를 사용해야합니다.

    public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor)
    

    예를 들면 :

    AsyncRestTemplate template = new AsyncRestTemplate(new ConcurrentTaskExecutor(Executors.newCachedThreadPool()));
    

    이 ExecutorService (Executors.newCachedThreadPool ())는 필요에 따라 새 스레드를 만들고 사용 가능한 경우 이전에 생성 된 스레드를 다시 사용합니다.

    아니면 더 나은, 당신은 다른 RequestFactory를 사용할 수 있습니다. 예를 들어 HttpComponentsAsyncClientHttpRequestFactory를 사용할 수 있습니다. 내부적으로 NIO를 사용하며 AsyncRestTemplate의 적절한 생성자를 호출합니다.

    new AsyncRestTemplate(new HttpComponentsAsyncClientHttpRequestFactory())
    

    AsyncRestTemplate의 내부 동작은 개체를 만드는 방법에 따라 다릅니다.

  2. from https://stackoverflow.com/questions/29380653/how-to-cancel-asyncresttemplate-http-request-if-they-are-taking-too-much-time by cc-by-sa and MIT license