Spring Boot中如何处理大量HTTP GET请求以降低整体接口调用耗时
批量HTTP请求耗时优化方案
核心优化思路:利用WebClient异步特性并发请求
你当前使用的Spring WebClient是反应式非阻塞客户端,天生支持异步并发请求,不需要额外引入同步线程池,仅调整批量请求组装逻辑即可大幅压缩总耗时。
具体实现步骤
- 首先调整HttpClient连接池配置,默认连接池最大连接数仅为256,可根据目标API限流规则调整到合适值,同时优化超时时间:
HttpClient client = HttpClient.create() // 调整连接池最大连接数,可根据API限流规则灵活调整 .resolver(spec -> spec.queryTimeout(Duration.ofSeconds(10))) .responseTimeout(Duration.ofSeconds(10)) // 单请求平均耗时6秒,设置10秒超时足够,无需保留60秒配置 .followRedirect(true);
- 批量转换1676条GeoInfo参数为请求任务,通过Flux控制并发度执行,避免触发API限流:
// 1. 拿到全量请求参数列表 List<GeoInfo> geoList = gs.getGeoXY(); // 2. 批量构造请求,设置并发度为200(可根据API限流阈值灵活调整) Flux.fromIterable(geoList) .flatMap(geo -> { // 每个坐标对应生成一个独立请求 return wc.get() .uri(uriBuilder -> uriBuilder.path("/getVilageFcst") .queryParam("serviceKey", serviceKey) .queryParam("numOfRows", numOfRows) .queryParam("pageNo", pageNo) .queryParam("dataType", dataType) .queryParam("base_date", base_date) .queryParam("base_time", base_time) .queryParam("nx", geo.getNx()) .queryParam("ny", geo.getNy()) .build()) .retrieve() .onStatus(HttpStatus::is4xxClientError, error -> Mono.error(new RuntimeException("API请求错误:" + error.statusCode()))) .onStatus(HttpStatus::is5xxServerError, error -> Mono.error(new RuntimeException("服务端错误:" + error.statusCode()))) .bodyToMono(SweatherRootRes.class) // 单请求出错不中断整体任务,打印日志后继续执行后续请求 .doOnError(e -> logger.error("请求坐标nx:{}, ny:{}失败,原因:{}", geo.getNx(), geo.getNy(), e.getMessage())) .onErrorResume(e -> Mono.empty()); }, 200) // 此处200为并发请求数,可根据实际情况调整 .doOnNext(res -> { // 单请求成功后的处理逻辑 if(res.getResponse().getBody()!= null) { // 注意不要使用单例注入的Temperature对象,每个请求独立创建对象避免数据覆盖 Temperature temp = new Temperature(); Items items = res.getResponse().getBody().getItems(); int count = 0; for(Item item : items.getItem()) { if(count >= 2 ) break; if(item.getCategory().equals("TMX")) { temp.setBestMax(item.getFcstValue()); temp.setNx(item.getNx()); temp.setNy(item.getNy()); temp.setDate(item.getBaseDate()); count++; } if(item.getCategory().equals("TMN")){ temp.setBestMin(item.getFcstValue()); count++; } } logger.info("坐标nx:{}, ny:{} 温度数据:{}", temp.getNx(), temp.getNy(), temp.toString()); // 此处可补充持久化逻辑,将温度数据写入数据库 } }) // 阻塞等待所有请求执行完成 .blockLast();
耗时预估
如果并发度设置为200,1676次请求仅需要9轮即可跑完,总耗时约为9*6=54秒,相比原有3小时的执行时长提升非常明显。
注意事项
- 并发度不要超过目标API的限流阈值,不确定限流规则时可先调低到50测试,避免被API提供商封禁IP
- 原有代码中注入的Temperature、SweatherRootRes是单例对象,批量请求时必须每个请求独立创建新对象,否则会出现数据覆盖问题
- 可补充请求重试机制,对超时、异常的请求重试1-2次,提升请求成功率
内容的提问来源于stack exchange,提问作者codingRookie
相关产品推荐
相关产品推荐

