[英]Concurrently call several Spring microservice URLs
I have a Spring Boot application that will call several microservice URLs using the GET
method.我有一个 Spring 引导应用程序,它将使用
GET
方法调用多个微服务 URL。 These microservice URL endpoints are all implemented as @RestController
s.这些微服务 URL 端点都实现为
@RestController
。 They don't return Flux
or Mono
.他们不返回
Flux
或Mono
。
I need my application to capture which URLs are not returning 2xx HTTP status.我需要我的应用程序来捕获哪些 URL没有返回 2xx HTTP 状态。
I'm currently using the following code to do this:我目前正在使用以下代码来执行此操作:
List<String> failedServiceUrls = new ArrayList<>();
for (String serviceUrl : serviceUrls.getServiceUrls()) {
try {
ResponseEntity<String> response = rest.getForEntity(serviceUrl, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
failedServiceUrls.add(serviceUrl);
}
} catch (Exception e){
failedServiceUrls.add(serviceUrl);
}
}
// all checks are complete so send email with the failedServiceUrls.
mail.sendEmail("Service Check Complete", failedServiceUrls);
}
The problem is that each URL call is slow to respond and I have to wait for one URL call to complete prior to making the next one.问题是每个 URL 调用响应缓慢,我必须等待一个 URL 调用完成,然后再进行下一个调用。
How can I change this to make the URLs calls be made concurrently?如何更改此设置以同时进行 URL 调用? After all call have completed, I need to send an email with any URLs that have an error that should be collected in
failedServiceUrls
.在所有调用完成后,我需要发送一个 email ,其中包含应在
failedServiceUrls
中收集的任何错误 URL。
Update更新
I revised the above post to state that I just want the calls to be made concurrently.我将上面的帖子修改为 state,我只想同时进行通话。 I don't care that
rest.getForEntity
call blocks.我不在乎
rest.getForEntity
调用块。
Using the executor service in your code, you can call all microservices in parallel this way:在您的代码中使用执行器服务,您可以通过这种方式并行调用所有微服务:
// synchronised it as per Maciej's comment:
failedServiceUrls = Collections.synchronizedList(failedServiceUrls);
ExecutorService executorService = Executors.newFixedThreadPool(serviceUrls.getServiceUrls().size());
List<Callable<String>> runnables = new ArrayList<>().stream().map(o -> new Callable<String>() {
@Override
public String call() throws Exception {
ResponseEntity<String> response = rest.getForEntity(serviceUrl, String.class);
// do something with the response
if (!response.getStatusCode().is2xxSuccessful()) {
failedServiceUrls.add(serviceUrl);
}
return response.getBody();
}
}).collect(toList());
List<Future<String>> result = executorService.invokeAll(runnables);
for(Future f : result) {
String resultFromService = f.get(); // blocker, it will wait until the execution is over
}
If you just want to make calls concurrently and you don't care about blocking threads you can:如果您只想同时进行调用并且您不关心阻塞线程,您可以:
Mono#fromCallable
Mono#fromCallable
包装阻塞服务调用serviceUrls.getServiceUrls()
into a reactive stream using Flux#fromIterable
Flux#fromIterable
将serviceUrls.getServiceUrls()
转换为反应式 streamFlux#filterWhen
using Flux from 2. and asynchronous service call from 1.Flux#filterWhen
使用 2 中的 Flux 和 1 中的异步服务调用同时调用和过滤失败的服务。Flux#collectList
and send email with invalid urls in subscribe
Flux#collectList
等待所有调用完成并发送 email subscribe
中的 URL 无效void sendFailedUrls() {
Flux.fromIterable(erviceUrls.getServiceUrls())
.filterWhen(url -> responseFailed(url))
.collectList()
.subscribe(failedURls -> mail.sendEmail("Service Check Complete", failedURls));
}
Mono<Boolean> responseFailed(String url) {
return Mono.fromCallable(() -> rest.getForEntity(url, String.class))
.map(response -> !response.getStatusCode().is2xxSuccessful())
.subscribeOn(Schedulers.boundedElastic());
}
Blocking calls with Reactor使用 Reactor 阻塞调用
Since the underlying service call is blocking it should be executed on a dedicated thread pool.由于底层服务调用是阻塞的,它应该在专用线程池上执行。 Size of this thread pool should be equal to the number of concurrent calls if you want to achieve full concurrency.
如果要实现完全并发,这个线程池的大小应该等于并发调用的数量。 That's why we need
.subscribeOn(Schedulers.boundedElastic())
这就是为什么我们需要
.subscribeOn(Schedulers.boundedElastic())
See: https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking请参阅: https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking
Better solution using WebClient使用 WebClient 的更好解决方案
Note however, that blocking calls should be avoided when using reactor and spring webflux.但是请注意,在使用 reactor 和 spring webflux 时应避免阻塞调用。 The correct way to do this would be to replace
RestTemplate
with WebClient
from Spring 5 which is fully non-blocking.正确的方法是用 Spring 5 中的
WebClient
替换RestTemplate
,这是完全非阻塞的。
See: https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/html/boot-features-webclient.html请参阅: https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/html/boot-features-webclient.ZFC35FDC70D5FC69D2639883A8227C
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.