복붙노트

[SPRING] 봄 RestTemplate - 비동기 대 sync restTemplate

SPRING

봄 RestTemplate - 비동기 대 sync restTemplate

RestTemplate과 AsyncRestTemplate 동기화 성능을 테스트하기 위해 다음 코드를 작성했습니다. POSTMAN에서 수 차례 수동으로 실행했습니다.

우리는 10 개의 참조를 GET 호출로 전달하기 때문에 10 개의 링크를 반환 할 수 있습니다.

RestTemplate - 동기식 및 2806ms의 반환 :

ArrayList<String> references = new ArrayList<>();
ArrayList<String> links = new ArrayList<>();
RestTemplate restTemplate = new RestTemplate(); 
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
for (int i = 0; i < 10; i++) {
    ResponseEntity<String> resource = restTemplate.getForEntity(references.get(i), String.class);
    links.add(resource.getBody().toString());
}

RestTemplate - 2794ms에서 비동기 및 리턴 :

//Creating a synchronizedList so that when the async resttemplate returns, there will be no concurrency issues
List<String> links = Collections.synchronizedList(new ArrayList<String>());

//CustomClientHttpRequestFactory just extends SimpleClientHttpRequestFactory but disables automatic redirects in SimpleClientHttpRequestFactory
CustomClientHttpRequestFactory customClientHttpRequestFactory = new CustomClientHttpRequestFactory();
//Setting the ThreadPoolTaskExecutor for the Async calls
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor pool = new org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor();
pool.setCorePoolSize(5);
pool.setMaxPoolSize(10);
pool.setWaitForTasksToCompleteOnShutdown(true);
pool.initialize();
//Setting the TaskExecutor to the ThreadPoolTaskExecutor
customClientHttpRequestFactory.setTaskExecutor(pool);

ArrayList<String> references = new ArrayList<>();
ArrayList<String> links = new ArrayList<>();
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(customClientHttpRequestFactory); 
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
for (int i = 0; i < 10; i++) {
    Future<ResponseEntity<String>> resource = asyncRestTemplate.getForEntity(references.get(i), String.class);
    ResponseEntity<String> entity = resource.get(); //this should start up 10 threads to get the links asynchronously
    links.add(entity.getBody().toString());
}

대부분의 경우 두 가지 방법 모두 실제로 비동기 및 동기화 호출에서 평균 2800ms의 결과를 반환합니다.

비동기 호출이 훨씬 더 빠를 것으로 예상했기 때문에 잘못된 것을하고 있습니까?

해결법

  1. ==============================

    1.나는 여기서 AsyncRest의 진정한 이점을 놓치고 있다고 말할 것이다. 전송중인 각 요청에 콜백을 추가하여 응답이 가능한 경우에만 처리되도록해야합니다.

    나는 여기서 AsyncRest의 진정한 이점을 놓치고 있다고 말할 것이다. 전송중인 각 요청에 콜백을 추가하여 응답이 가능한 경우에만 처리되도록해야합니다.

    실제로 AsyncRestTemplate의 getForEntity 메서드는 콜백 작업에 연결할 수있는 ListenableFuture를 반환합니다. 더 자세한 정보는 공식 문서 ListenableFuture를보십시오.

    예를 들어 귀하의 경우 :

    for (int i = 0; i < 10; i++) {
         ListenableFuture<ResponseEntity<String>> response = asyncRestTemplate.getForEntity(references.get(i), String.class);
         response.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
                @Override
                public void onSuccess(ResponseEntity<String> result) {
                    // Do stuff onSuccess 
                    links.add(result.getBody().toString());
                }
    
                @Override
                public void onFailure(Throwable ex) {
                    log.warn("Error detected while submitting a REST request. Exception was {}", ex.getMessage());
                }
            });
    }
    
  2. ==============================

    2.Java Future의 까다로운 점은 구성 할 수 없으며 차단하기가 쉽다는 점입니다.

    Java Future의 까다로운 점은 구성 할 수 없으며 차단하기가 쉽다는 점입니다.

    이 경우, future.get ()을 호출하면 코드 블록이 작성되고 응답이 돌아올 때까지 대기합니다. 사실,이 접근 방식은 순차적 호출을 만들고이 RestTemplate 구현의 비동기 특성을 활용하지 않습니다.

    이 문제를 해결하는 가장 간단한 방법은 두 개의 루프로 분리하는 것입니다.

    ArrayList<Future<ResponseEntity<String>>> futures = new ArrayList<>();
    
    for (String url : references.get()) {
        futures.add(asyncRestTemplate.getForEntity(url, String.class)); //start up to 10 requests in parallel, depending on your pool
    }
    
    for (Future<ResponseEntity<String>> future : futures) {
        ResponseEntity<String> entity = future.get(); // blocking on the first request
        links.add(entity.getBody().toString());
    }
    

    분명히 JDK8 스트림, lambdas 및 ListenableFuture / CompletableFuture 또는 컴포지션 라이브러리를 사용하는 경우 더욱 세련된 솔루션이 있습니다.

  3. from https://stackoverflow.com/questions/31572475/spring-resttemplate-async-vs-sync-resttemplate by cc-by-sa and MIT license