【Java分享客栈】一文搞定CompletableFuture并行处理,成倍缩短查询时间。
2022/4/27 14:12:47
本文主要是介绍【Java分享客栈】一文搞定CompletableFuture并行处理,成倍缩短查询时间。,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
工作中你可能会遇到很多这样的场景,一个接口,要从其他几个service调用查询方法,分别获取到需要的值之后再封装数据返回。
还可能在微服务中遇到类似的情况,某个服务的接口,要使用好几次feign去调用其他服务的方法获取数据,最后拿到想要的值并封装返回给前端。
这样的场景下,当某个或多个rpc调用的方法比较耗时,整个接口的响应就会非常慢。Java8之后,有一个工具非常适合处理这种场景,就是CompletableFuture。
场景
本章主要讲解CompletableFuture的并行处理用法,来针对这种很常见的场景,帮助大家快速掌握并应用到实际工作当中。CompletableFuture内部的用法还有许多,但个人用到的场景大多都是并行处理,对其他场景感兴趣的小伙伴可以另行百度搜索。
场景说明:
写一个接口,调用另外两个HTTP接口,分别获取二十四节气和星座,最后放在一起返回。
用法
1、在线API
我们访问极速数据网站https://www.jisuapi.com ,注册一个账号,就可以免费使用里面的一些在线API,平均每天有100次免费机会,对于我这样经常本地做一些测试的人来说完全够用了。
这里,我使用了其中的查询二十四节气API,和查询星座API,后面会提供案例代码,也可以直接使用我的。
2、编写在线API查询
这里,我们在查询时,模拟耗时的情况。
1)、查询二十四节气
package com.example.async.service; import cn.hutool.http.HttpUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * <p> * 查询二十四节气的服务 * </p> * * @author 福隆苑居士,公众号:【Java分享客栈】 * @since 2022-04-26 15:25 */ @Service @Slf4j public class TwentyFourService { public static final String APPKEY = "xxxxxx";// 你的appkey public static final String URL = "https://api.jisuapi.com/jieqi/query"; public String getResult() { String url = URL + "?appkey=" + APPKEY; String result = HttpUtil.get(url); // 模拟耗时 try { TimeUnit.SECONDS.sleep(5); } catch (Exception e) { log.error("[二十四节气]>>>> 异常: {}", e.getMessage(), e); } return result; } }
2)、查询星座
package com.example.async.service; import cn.hutool.http.HttpUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * <p> * 查询星座的服务 * </p> * * @author 福隆苑居士,公众号:【Java分享客栈】 * @since 2022-04-26 15:25 */ @Service @Slf4j public class ConstellationService { public static final String APPKEY = "xxxxxx";// 你的appkey public static final String URL = "https://api.jisuapi.com/astro/all"; public String getResult() { String url = URL + "?appkey=" + APPKEY; String result = HttpUtil.get(url); // 模拟耗时 try { TimeUnit.SECONDS.sleep(5); } catch (Exception e) { log.error("[星座]>>>> 异常: {}", e.getMessage(), e); } return result; } }
3、编写查询服务
package com.example.async.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; /** * <p> * 查询服务 * </p> * * @author 福隆苑居士,公众号:【Java分享客栈】 * @since 2022-04-26 17:38 */ @Service @Slf4j public class QueryService { private final TwentyFourService twentyFourService; private final ConstellationService constellationService; public QueryService(TwentyFourService twentyFourService, ConstellationService constellationService) { this.twentyFourService = twentyFourService; this.constellationService = constellationService; } /** * 同步返回结果 * @return 结果 */ public Map<String, Object> query() { // 1、查询二十四节气 String twentyFourResult = twentyFourService.getResult(); // 2、查询星座 String constellationResult = constellationService.getResult(); // 3、返回 Map<String, Object> map = new HashMap<>(); map.put("twentyFourResult", twentyFourResult); map.put("constellationResult", constellationResult); return map; } }
4、编写测试接口
这里,我们专门加上了耗时计算。
package com.example.async.controller; import cn.hutool.core.date.TimeInterval; import com.example.async.service.QueryService; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; /** * <p> * 测试 * </p> * * @author 福隆苑居士,公众号:【Java分享客栈】 * @since 2022-04-26 17:35 */ @RestController @RequestMapping("/api") @Slf4j public class TestController { private final QueryService queryService; public TestController(QueryService queryService) { this.queryService = queryService; } /** * 同步查询 * @return 结果 */ @GetMapping("/query") public ResponseEntity<Map<String, Object>> query() { // 计时 final TimeInterval timer = new TimeInterval(); timer.start(); Map<String, Object> map = queryService.query(); map.put("costTime", timer.intervalMs() + " ms"); return ResponseEntity.ok().body(map); } }
5、效果
可以看到,两个接口一共耗费了10秒左右才返回。
6、CompletableFuture并行查询
现在我们来使用CompletableFuture改造下接口,并行查询两个HTTP接口再返回。
package com.example.async.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; /** * <p> * 查询服务 * </p> * * @author 福隆苑居士,公众号:【Java分享客栈】 * @since 2022-04-26 17:38 */ @Service @Slf4j public class QueryService { private final TwentyFourService twentyFourService; private final ConstellationService constellationService; public QueryService(TwentyFourService twentyFourService, ConstellationService constellationService) { this.twentyFourService = twentyFourService; this.constellationService = constellationService; } /** * 异步返回结果 * @return 结果 */ public Map<String, Object> queryAsync() { Map<String, Object> map = new HashMap<>(); // 1、查询二十四节气 CompletableFuture<String> twentyFourQuery = CompletableFuture.supplyAsync(twentyFourService::getResult); twentyFourQuery.thenAccept((result) -> { log.info("查询二十四节气结果:{}", result); map.put("twentyFourResult", result); }).exceptionally((e) -> { log.error("查询二十四节气异常: {}", e.getMessage(), e); map.put("twentyFourResult", ""); return null; }); // 2、查询星座 CompletableFuture<String> constellationQuery = CompletableFuture.supplyAsync(constellationService::getResult); constellationQuery.thenAccept((result) -> { log.info("查询星座结果:{}", result); map.put("constellationResult", result); }).exceptionally((e) -> { log.error("查询星座异常: {}", e.getMessage(), e); map.put("constellationResult", ""); return null; }); // 3、allOf-两个查询必须都完成 CompletableFuture<Void> allQuery = CompletableFuture.allOf(twentyFourQuery, constellationQuery); CompletableFuture<Map<String, Object>> future = allQuery.thenApply((result) -> { log.info("------------------ 全部查询都完成 ------------------ "); return map; }).exceptionally((e) -> { log.error(e.getMessage(), e); return null; }); // 获取异步方法返回值 // get()-内部抛出了异常需手动处理; join()-内部处理了异常无需手动处理,点进去一看便知。 future.join(); return map; } }
7、编写测试接口
package com.example.async.controller; import cn.hutool.core.date.TimeInterval; import com.example.async.service.QueryService; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; /** * <p> * 测试 * </p> * * @author 福隆苑居士,公众号:【Java分享客栈】 * @since 2022-04-26 17:35 */ @RestController @RequestMapping("/api") @Slf4j public class TestController { private final QueryService queryService; public TestController(QueryService queryService) { this.queryService = queryService; } /** * 异步查询 * @return 结果 */ @GetMapping("/queryAsync") public ResponseEntity<Map<String, Object>> queryAsync() { // 计时 final TimeInterval timer = new TimeInterval(); timer.start(); Map<String, Object> map = queryService.queryAsync(); map.put("costTime", timer.intervalMs() + " ms"); return ResponseEntity.ok().body(map); } }
8、CompletableFuture效果
可以看到,时间缩短了一倍。
9、思考
如果在微服务中,有一个很复杂的业务需要远程调用5个第三方laji厂家的接口,每个接口假设都耗时5秒,使用CompletableFuture并行处理最终需要多久?
答案是肯定的,同步查询需要25秒左右,CompletableFuture并行处理还是5秒左右,也就是说,同一个接口中,调用的耗时接口越多,CompletableFuture优化的幅度就越大。
示例代码
可以下载我的完整示例代码本地按需测试,里面有我的极速数据API的key,省得自己注册账号了,每天免费就100次,先到先得哦。
链接:https://pan.baidu.com/doc/share/P_Jn_x22fos0ED3YEnqI8A-232386145447394
提取码:piil
觉得有帮助的话,就请顺手点个【推荐】吧,本人定期分享工作中的经验及趣事,原创文章均为手打,喜欢的话也可以关注一下哦~
这篇关于【Java分享客栈】一文搞定CompletableFuture并行处理,成倍缩短查询时间。的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南