Spring Webflux中WebClient如何并行发送HTTP请求?
Hey there! Let's break down your problem step by step:
1. Is your current code actually running sequentially?
Yes, it is. But the culprit isn't Mono.zip—it's how you're constructing the three company request Monos.
2. Why is it sequential?
Each of your areYouInterestedToBuyCompanyX methods wraps the company request inside a flatMap on the priceMustBeComputedFirst Mono. Even though you've cached the price result, when Mono.zip subscribes to the three company Monos, the flatMap callbacks for each company get triggered one after another. This means your WebClient requests to Company A, B, C are initiated in sequence, not in parallel—hence the order you're seeing in the service logs.
3. How to fix it for parallel execution
The key is to create all three company request Monos after the price has been retrieved, and then subscribe to them all at once. Here's the adjusted code with additional optimizations:
@RestController public class QuestionController { // Reuse a base WebClient instance instead of creating new ones repeatedly private final WebClient baseWebClient; @Autowired public QuestionController(WebClient.Builder webClientBuilder) { this.baseWebClient = webClientBuilder.baseUrl("http://some-service:8111").build(); } @GetMapping("/question") public Mono<String> question(@RequestParam(value = "foo", required = false) String foo) { // Step 1: Fetch the price first return firstComputePriceBySendingRequest(foo) .flatMap(price -> { // Now that we have the price, create all three company request Monos Mono<String> companyA = areYouInterestedToBuyCompanyA(price); Mono<String> companyB = areYouInterestedToBuyCompanyB(price); Mono<String> companyC = areYouInterestedToBuyCompanyC(price); // Zip them together—this subscribes to all three at once, triggering parallel requests return Mono.zip(companyA, companyB, companyC) .map(tuple3 -> computeSomethingAtTheEnd(tuple3.getT1(), tuple3.getT2(), tuple3.getT3())); }); } private Mono<String> firstComputePriceBySendingRequest(String foo) { return baseWebClient.get() .uri("/getPriceForFoo/{foo}", foo) .retrieve() .bodyToMono(String.class); } private Mono<String> areYouInterestedToBuyCompanyA(String price) { return baseWebClient.mutate() .baseUrl("http://companyA") .build() .get() .uri("/areYouInterestedToBuy/{product}", price) .retrieve() .bodyToMono(String.class); } private Mono<String> areYouInterestedToBuyCompanyB(String price) { return baseWebClient.mutate() .baseUrl("http://companyB") .build() .get() .uri("/areYouInterestedToBuy/{product}", price) .retrieve() .bodyToMono(String.class); } private Mono<String> areYouInterestedToBuyCompanyC(String price) { return baseWebClient.mutate() .baseUrl("http://companyC") .build() .get() .uri("/areYouInterestedToBuy/{product}", price) .retrieve() .bodyToMono(String.class); } private String computeSomethingAtTheEnd(String t1, String t2, String t3) { return "some computation based on each company's response"; } }
Extra Tips for Better Practice
- Reuse WebClient instances: WebClient is designed to be thread-safe and reusable. For even better efficiency, you could pre-create dedicated WebClient beans for Company A, B, and C in your configuration class instead of mutating the base client every time.
- No more
cache()needed: By nesting the company requests inside the price Mono'sflatMap, we only execute the price request once—so thecache()call is no longer necessary.
Why this works
When the price Mono completes, we create all three company request Monos in one batch. Mono.zip then subscribes to all three simultaneously, which tells Reactor to initiate all three WebClient requests in parallel. You'll now see the companies receive requests at roughly the same time, instead of in sequence.
内容的提问来源于stack exchange,提问作者PatPanda

