You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 2 WebClient:单Controller内并行调用多个Rest API方案咨询

Spring Boot 单个请求内并行调用下游服务实现方案

核心实现思路是将每个下游服务调用封装为独立异步任务,提交到自定义线程池并行执行,待所有任务执行完成后聚合结果返回。

方案1:原生JDK CompletableFuture 实现(无额外依赖,通用性最强)

第一步:配置自定义业务线程池

避免使用JDK默认公共ForkJoinPool,防止和其他异步任务竞争资源,配置示例:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class ThreadPoolConfig {
    @Bean("bizRestExecutor")
    public Executor bizRestExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数根据实际业务调整,推荐按:核心数 = 2 * CPU核心数 + 下游服务平均耗时/CPU计算耗时 估算
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("rest-call-");
        // 拒绝策略根据业务选择,此处用CallerRunsPolicy避免任务丢弃
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

第二步:Controller层并行调用实现

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

@RestController
public class MyController {
    @Resource(name = "bizRestExecutor")
    private Executor bizRestExecutor;
    // 假设你已经注入了RestTemplate用于下游调用
    @Resource
    private RestTemplate restTemplate;

    @GetMapping
    public MyResponse method() {
        // 提交第一个下游调用任务,异步执行
        CompletableFuture<Resp1> resp1Future = CompletableFuture.supplyAsync(() -> 
                restTemplate.getForObject("endpoint1", Resp1.class), bizRestExecutor);
        // 提交第二个下游调用任务,异步执行
        CompletableFuture<Resp2> resp2Future = CompletableFuture.supplyAsync(() -> 
                restTemplate.getForObject("endpoint2", Resp2.class), bizRestExecutor);

        // 等待所有任务执行完成,可添加超时时间避免无限等待,此处示例超时时间3秒
        CompletableFuture.allOf(resp1Future, resp2Future).get(3, TimeUnit.SECONDS);

        // 聚合结果返回
        Resp1 resp1 = resp1Future.join();
        Resp2 resp2 = resp2Future.join();
        return new MyResponse(resp1, resp2);
    }
}

方案2:Spring @Async 注解实现(代码更解耦,适合调用逻辑复杂场景)

第一步:开启异步注解支持

在启动类或者配置类上加@EnableAsync注解。

第二步:封装下游调用为异步方法

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.CompletableFuture;

@Service
public class DownstreamCallService {
    @Resource
    private RestTemplate restTemplate;

    @Async("bizRestExecutor") // 指定用我们自定义的线程池
    public CompletableFuture<Resp1> callEndpoint1() {
        Resp1 resp = restTemplate.getForObject("endpoint1", Resp1.class);
        return CompletableFuture.completedFuture(resp);
    }

    @Async("bizRestExecutor")
    public CompletableFuture<Resp2> callEndpoint2() {
        Resp2 resp = restTemplate.getForObject("endpoint2", Resp2.class);
        return CompletableFuture.completedFuture(resp);
    }
}

第三步:Controller层调用

@RestController
public class MyController {
    @Resource
    private DownstreamCallService downstreamCallService;

    @GetMapping
    public MyResponse method() throws Exception {
        CompletableFuture<Resp1> resp1Future = downstreamCallService.callEndpoint1();
        CompletableFuture<Resp2> resp2Future = downstreamCallService.callEndpoint2();
        
        CompletableFuture.allOf(resp1Future, resp2Future).get(3, TimeUnit.SECONDS);
        return new MyResponse(resp1Future.join(), resp2Future.join());
    }
}

方案3:响应式WebFlux实现(仅适合已使用WebFlux响应式栈的项目)

如果项目用WebFlux替代了Spring MVC,可以直接用Mono.zip实现并行调用,无需额外线程池配置:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import java.time.Duration;

@RestController
public class MyController {
    @Resource
    private WebClient webClient;

    @GetMapping
    public Mono<MyResponse> method() {
        Mono<Resp1> resp1Mono = webClient.get().uri("endpoint1").retrieve().bodyToMono(Resp1.class);
        Mono<Resp2> resp2Mono = webClient.get().uri("endpoint2").retrieve().bodyToMono(Resp2.class);
        
        return Mono.zip(resp1Mono, resp2Mono)
                .map(tuple -> new MyResponse(tuple.getT1(), tuple.getT2()))
                .timeout(Duration.ofSeconds(3));
    }
}

注意事项

  • 所有异步调用必须加超时控制,避免下游服务故障导致本服务线程阻塞耗尽
  • 线程池参数需根据实际业务压测结果调整,不要直接照搬示例配置
  • 单个下游调用失败需做好降级兜底,可在CompletableFuture后加exceptionally方法返回默认值,避免单个接口失败导致整个请求报错
  • 如果下游调用数量较多,建议批量提交任务后统一等待,不要逐个调用join

内容的提问来源于stack exchange,提问作者Uva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 11:27:01