簡體   English   中英

Spring RestTemplate - 異步vs同步restTemplate

[英]Spring RestTemplate - async vs 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。

我做錯了什么,因為我希望異步調用更快?

我會說你在這里錯過了AsyncRest的真正好處。 您應該為要發送的每個請求添加回調,以便響應僅在可用時進行處理。

實際上, AsyncRestTemplategetForEntity方法返回一個可以連接回調任務的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());
            }
        });
}

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或組合庫。

如今, AsyncRestTemplate@Deprecated支持WebClient 所以沒有人應該再使用那門課了!

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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