2015-07-22 4 views
5

Я написал следующий код, чтобы проверить производительность как Synt RestTemplate, так и AsyncRestTemplate. Я просто запускал его несколько раз вручную на POSTMAN.Spring RestTemplate - async vs sync restTemplate

Мы просто проездом 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()); 
} 

В большинстве случаев, оба метода фактически возвращают результаты с очень похожим временем, усредняя 2800 мс в обоих асинхронных и синхронных вызовах.

Я делаю что-то неправильное, поскольку я ожидал бы, что вызов асинхронного вызова будет намного быстрее?

ответ

4

Сложная вещь с 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 ручьев, лямбды и ListenableFuture/CompletableFuture или библиотеки композиции.

+0

Привет, спасибо за ваш ответ. Я действительно задаюсь вопросом, но когда мы вызываем ResponseEntity entity = future.get() в вашем коде, не блокирует ли этот код так, чтобы цикл for не продолжался до получения ответа? Я вижу незначительное улучшение во времени, когда вызов возвращается в 2500 мс или около того, но он не является существенным. – Simon

+0

yes, future.get() блокирует, но в этот момент все запросы уже отправлены. Если вы можете использовать JDK8 CompletableFutures или другую библиотеку композиций, у вас может быть что-то более эффективное. Измеряя это, имейте в виду, что создание RestTemplate/AsyncRestTemplate требует времени и ресурсов и должно выполняться один раз (и не должно учитываться в вашем таймере) –

3

Я бы сказал, что здесь отсутствуют реальные преимущества AsyncRest. Вы должны добавить обратные вызовы к каждому отправляемому запросу, чтобы ответ был процессом только тогда, когда он был доступен.

Действительно, метод getForEntity для AsyncRestTemplate возвращает 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()); 
      } 
     }); 
}