Java Spring RestTemplate - 异步 vs 同步 restTemplate
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31572475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring RestTemplate - async vs sync restTemplate
提问by Simon
I wrote the following code to test the performance of both the sync RestTemplate and AsyncRestTemplate. I just ran it a few times manually on POSTMAN.
我编写了以下代码来测试同步 RestTemplate 和 AsyncRestTemplate 的性能。我只是在 POSTMAN 上手动运行了几次。
We are just passing 10 references into a GET call so that we can return 10 links:
我们只是将 10 个引用传递到 GET 调用中,以便我们可以返回 10 个链接:
RestTemplate - synchronous and returns in 2806ms:
RestTemplate - 同步并在 2806 毫秒内返回:
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 - asynchronous and returns in 2794ms:
RestTemplate - 异步并在 2794 毫秒内返回:
//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());
}
In most cases, both methods actually return back the results with a very similar time, averaging 2800ms in both async and sync calls.
在大多数情况下,这两种方法实际上以非常相似的时间返回结果,异步和同步调用的平均时间为 2800 毫秒。
Am I doing something incorrect as I would have expected the async call to be much faster?
我是否在做一些不正确的事情,因为我期望异步调用会快得多?
回答by Brian Clozel
The tricky thing with Java Future
is that it's not composable and it's really easy to block.
Java 的棘手之Future
处在于它不可组合,而且很容易被阻塞。
In this case, calling future.get()
makes your code block and wait until the response is back. In fact, this approach makes sequential calls and does not leverage the async nature of this RestTemplate
implementation.
在这种情况下,调用future.get()
会使您的代码阻塞并等待响应返回。事实上,这种方法进行顺序调用,并且没有利用这种RestTemplate
实现的异步特性。
The simplest way to fix this is to separate it in two loops:
解决此问题的最简单方法是将其分成两个循环:
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());
}
Obviously there are more elegant solutions, especially if using JDK8 streams, lambdas and ListenableFuture/CompletableFuture or composition libraries.
显然有更优雅的解决方案,特别是如果使用 JDK8 流、lambdas 和 ListenableFuture/CompletableFuture 或组合库。
回答by Ugo Giordano
I would say that you're missing the real benefits of the AsyncResthere. You should add callbacks to each requests you're sending so that the response will be processes only when available.
我会说你在这里错过了AsyncRest的真正好处。您应该为您发送的每个请求添加回调,以便响应仅在可用时进行处理。
Indeed, the getForEntity
method of an AsyncRestTemplate
returns a ListenableFuture
to which you can connect a callback task. See the official doc ListenableFuturefor further information.
事实上,getForEntity
an的方法会AsyncRestTemplate
返回一个 ListenableFuture
您可以连接回调任务的 a 。有关更多信息,请参阅官方文档ListenableFuture。
For example in your case it could be:
例如,在您的情况下,它可能是:
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());
}
});
}
回答by membersound
Nowadays, AsyncRestTemplate
is @Deprecated
in favor of WebClient
. So nobody should use that class anymore!
如今,AsyncRestTemplate
是@Deprecated
赞成的WebClient
。所以没有人应该再使用那个类了!