我试图在微服务应用程序中调用外部服务,以并行获取所有响应,并在开始其他计算之前将它们组合起来。我知道我可以在每个Mono对象上使用block()调用,但是这样做会打消使用反应式api的目的。是否有可能并行触发所有请求并在一个点上合并它们。
示例代码如下。在这种情况下,将在实际响应出现之前打印“完成”。我也知道订阅电话是不阻塞的。
我希望在收集所有响应后打印“完成”,因此需要某种阻止。但是不想阻止每个请求
final List<Mono<String>> responseOne = new ArrayList<>();
IntStream.range(0, 10).forEach(i -> {
Mono<String> responseMono =
WebClient.create("https://jsonplaceholder.typicode.com/posts")
.post()
.retrieve()
.bodyToMono(String.class)
;
System.out.println("create mono response lazy initialization");
responseOne.add(responseMono);
});
Flux.merge(responseOne).collectList().subscribe( res -> {
System.out.println(res);
});
System.out.println("Done");
根据建议,我提出了这个建议,对我来说很有效。
StopWatch watch = new StopWatch();
watch.start();
final List<Mono<String>> responseOne = new ArrayList<>();
IntStream.range(0, 10).forEach(i -> {
Mono<String> responseMono =
WebClient.create("https://jsonplaceholder.typicode.com/posts")
.post()
.retrieve()
.bodyToMono(String.class);
System.out.println("create mono response lazy initialization");
responseOne.add(responseMono);
});
CompletableFuture<List<String>> futureCount = new CompletableFuture<>();
List<String> res = new ArrayList<>();
Mono.zip(responseOne, Arrays::asList)
.flatMapIterable(objects -> objects) // make flux of objects
.doOnComplete(() -> {
futureCount.complete(res);
}) // will be printed on completion of the flux created above
.subscribe(responseString -> {
res.add((String) responseString);
}
);
watch.stop();
List<String> response = futureCount.get();
System.out.println(response);
// do rest of the computation
System.out.println(watch.getLastTaskTimeMillis());
答案 0 :(得分:2)
Mono.zip
Done
因此,您可以按以下方式修改代码
final List<Mono<String>> responseMonos = IntStream.range(0, 10).mapToObj(
index -> WebClient.create("https://jsonplaceholder.typicode.com/posts").post().retrieve()
.bodyToMono(String.class)).collect(Collectors.toList()); // create iterable of mono of network calls
Mono.zip(responseMonos, Arrays::asList) // make parallel network calls and collect it to a list
.flatMapIterable(objects -> objects) // make flux of objects
.doOnComplete(() -> System.out.println("Done")) // will be printed on completion of the flux created above
.subscribe(responseString -> System.out.println("responseString = " + responseString)); // subscribe and start emitting values from flux
在反应式代码中显式调用subscribe
或block
也不是一个好主意。
答案 1 :(得分:0)
是否有可能并行触发所有请求并将它们组合在一个点上。
这正是您的代码已经在做的事情。如果您不相信我,请在.delayElement(Duration.ofSeconds(2))
通话后坚持使用bodyToMono()
。您会看到列表在2秒钟后打印出来,而不是20秒(这是顺序执行10次的结果。)
合并部分正在您的Flux.merge().collectList()
通话中。
在这种情况下,在实际响应出现之前会打印“完成”。
这是意料之中的,因为您的上一个System.out.println()
调用正在反应式回调链之外执行。如果您希望在打印列表后打印“完成”(您已经混淆了传递给您的s
调用方的使用者的变量名subscribe()
),则需要将其放入其中该消费者,而不是外部消费者。
如果您要使用命令式API,因此需要阻止列表,则可以执行以下操作:
List<String> list = Flux.merge(responseOne).collectList().block();
...它仍将并行执行调用(因此仍会为您带来一些好处),但随后阻塞直到所有调用完成并合并到一个列表中。 (但是,如果您只是将电抗器用于这种类型的用途,是否值得商while。)