Spring RestTemplate - 异步vs同步restTemplate

Sim*_*mon 13 java spring asynchronous 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());
}
Run Code Online (Sandbox Code Playgroud)

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());
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,两种方法实际上都以非常相似的时间返回结果,在异步和同步调用中平均为2800ms.

我做错了什么,因为我希望异步调用更快?

Ugo*_*ano 15

我会说你在这里错过了AsyncRest的真正好处.您应该为要发送的每个请求添加回调,以便响应仅在可用时进行处理.

实际上,返回a 的getForEntity方法可以连接回调任务.有关详细信息,请参阅官方文档ListenableFuture.AsyncRestTemplateListenableFuture

例如,在您的情况下,它可能是:

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());
            }
        });
}
Run Code Online (Sandbox Code Playgroud)


Bri*_*zel 8

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());
}
Run Code Online (Sandbox Code Playgroud)

显然有更优雅的解决方案,特别是如果使用JDK8流,lambdas和ListenableFuture/CompletableFuture或组合库.

  • 是的,future.get()阻止,但此时所有请求都已发送.如果您可以使用JDK8 CompletableFutures或其他组合库,则可以提高效率.在测量时,请记住创建RestTemplate/AsyncRestTemplate需要花费时间和资源,并且应该执行一次(并且不应该计入您的计时器) (2认同)

mem*_*und 7

如今,AsyncRestTemplate@Deprecated的支持WebClient。因此,没有人应该再使用该类!

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