在Spring Boot应用开发中,很多接口需要同时调用多个外部REST服务获取数据再聚合返回,串行调用这些服务会导致接口总耗时等于所有调用耗时之和,性能表现很差。通过并行REST调用可以让多个请求同时发起,总耗时接近耗时最长的单个请求,大幅提升接口响应速度。

并行REST调用的适用场景
并行调用并不是所有场景都适用,需要满足以下条件才能发挥效果:
- 多个REST调用之间没有依赖关系,不需要前一个请求的返回结果作为后一个请求的参数
- 接口总耗时主要来自多个外部服务的调用等待时间
- 服务器资源充足,能够支撑多线程并行处理
核心实现原理
Spring Boot中可以使用CompletableFuture配合异步线程池实现并行调用,CompletableFuture是Java 8引入的异步编程工具,支持将任务提交到线程池异步执行,并且可以方便地处理多个异步任务的结果聚合。
完整实现步骤
1. 配置异步线程池
首先需要配置自定义线程池,避免使用默认的ForkJoinPool,方便根据业务需求调整线程参数。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class AsyncConfig {
@Bean("restCallExecutor")
public Executor restCallExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
// 最大线程数
executor.setMaxPoolSize(20);
// 队列容量
executor.setQueueCapacity(100);
// 线程名前缀
executor.setThreadNamePrefix("rest-call-");
// 初始化线程池
executor.initialize();
return executor;
}
}
2. 定义REST调用服务类
使用RestTemplate发起REST请求,将每个调用封装为返回CompletableFuture的方法,并且指定使用上面配置的线程池执行异步任务。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
@Service
public class RemoteCallService {
@Autowired
private RestTemplate restTemplate;
// 异步调用第一个外部服务,获取用户信息
@Async("restCallExecutor")
public CompletableFuture<String> getUserInfo(String userId) {
String url = "http://ipipp.com/user/" + userId;
String result = restTemplate.getForObject(url, String.class);
return CompletableFuture.completedFuture(result);
}
// 异步调用第二个外部服务,获取用户订单信息
@Async("restCallExecutor")
public CompletableFuture<String> getUserOrders(String userId) {
String url = "http://ipipp.com/order?userId=" + userId;
String result = restTemplate.getForObject(url, String.class);
return CompletableFuture.completedFuture(result);
}
// 异步调用第三个外部服务,获取用户积分信息
@Async("restCallExecutor")
public CompletableFuture<String> getUserPoints(String userId) {
String url = "http://ipipp.com/points?userId=" + userId;
String result = restTemplate.getForObject(url, String.class);
return CompletableFuture.completedFuture(result);
}
}
3. 编写并行调用接口
在业务层同时发起多个异步调用,等待所有调用完成后再聚合结果返回。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@RestController
public class UserAggregateController {
@Autowired
private RemoteCallService remoteCallService;
@GetMapping("/user/aggregate/{userId}")
public String getUserAggregateInfo(@PathVariable String userId) throws ExecutionException, InterruptedException {
// 同时发起三个并行调用
CompletableFuture<String> userInfoFuture = remoteCallService.getUserInfo(userId);
CompletableFuture<String> userOrdersFuture = remoteCallService.getUserOrders(userId);
CompletableFuture<String> userPointsFuture = remoteCallService.getUserPoints(userId);
// 等待所有异步任务完成
CompletableFuture.allOf(userInfoFuture, userOrdersFuture, userPointsFuture).join();
// 获取各个调用的结果
String userInfo = userInfoFuture.get();
String userOrders = userOrdersFuture.get();
String userPoints = userPointsFuture.get();
// 聚合结果返回,实际业务中可封装为统一响应对象
return "用户信息:" + userInfo + ",订单信息:" + userOrders + ",积分信息:" + userPoints;
}
}
性能对比测试
假设三个外部服务的调用耗时分别为200ms、300ms、250ms,两种调用方式的耗时对比如下:
| 调用方式 | 总耗时 |
|---|---|
| 串行调用 | 200+300+250=750ms |
| 并行调用 | 接近最长耗时300ms |
注意事项
- 线程池参数需要根据实际业务并发量和服务器资源调整,避免线程数过多导致资源耗尽
- 如果并行调用中有某个请求必然失败,需要处理异常,避免影响其他结果的获取,可以在
CompletableFuture中增加异常处理逻辑 - 并行调用会增加服务器的线程开销,如果接口调用量非常大,需要评估线程池容量是否足够
- 如果外部服务有调用频率限制,并行调用可能会触发限流,需要提前确认外部服务的承受能力
异常处理优化示例
可以在异步方法中增加异常捕获,避免单个调用失败导致整体接口报错:
@Async("restCallExecutor")
public CompletableFuture<String> getUserInfo(String userId) {
try {
String url = "http://ipipp.com/user/" + userId;
String result = restTemplate.getForObject(url, String.class);
return CompletableFuture.completedFuture(result);
} catch (Exception e) {
// 异常时返回默认结果,避免影响整体聚合
return CompletableFuture.completedFuture("获取用户信息失败");
}
}
Spring_Boot并行REST调用接口性能优化CompletableFutureRestTemplate修改时间:2026-07-09 11:30:33