java-API并行调用

java-API并行调用-飞一样的编程
飞一样的编程
擅长邻域:Java,MySQL,Linux,nginx,springboot,mongodb,微信小程序,vue

2021-01-01 23:53:56   172浏览 分类: Java

今天分享的主题是:java-api并行调用
当数据量较大得时候,都会通过分库分表来拆分,分担读写得压力。分库分表后比较麻烦得就是查询得问题,如果不是直接根据分片键去查询得话,需要对多个表进行查询。

在一些复杂得业务场景下,比如订单搜索,除了订单号,用户,商家 这些常用得搜索条件,可能还有时间,商品等等。

目前常见得做法将数据同步到ES这类搜索框架中进行查询,然后通过搜出来得结果,一般是主键ID, 再去具体得数据表中查询完整得数据,组装返回给调用方。

比如下面这段代码,首先查询出文章信息,然后根据文章中得用户ID去查询用户得昵称。
List articleBos = articleDoPage.getRecords().stream().map(r -> {
String nickname = userManager.getNickname(r.getUserId());
return articleBoConvert.convertPlus(r, nickname);
}).collect(Collectors.toList());
如果文章有10条数据,那么就需要调用10次用户服务提供得接口,而且是同步调用操作。

当然我们也可以用并行流来实现并发调用,代码如下:
List articleBos = articleDoPage.getRecords().parallelStream().map(r -> {
String nickname = userManager.getNickname(r.getUserId());
return articleBoConvert.convertPlus(r, nickname);
}).collect(Collectors.toList());
并行流得优点很明显,代码不用做特别大得改动。需要注意如果用并行流,最好单独定义一个ForkJoinPool。

除了用并行流,还可以使用批量查询得方式来提高性能,降低RPC得调用次数,代码如下:
List userIds = articleDoPage.getRecords().stream().map(article -> article.getUserId()).collect(Collectors.toList());
Map nickNameMap = userManager.queryByIds(userIds).stream().collect(Collectors.toMap(UserResponse::getId, UserResponse::getNickname));
List articleBos = articleDoPage.getRecords().stream().map(r -> {
String nickname = nickNameMap.containsKey(r.getUserId()) ? nickNameMap.get(r.getUserId()) : CommonConstant.DEFAULT_EMPTY_STR;
return articleBoConvert.convertPlus(r, nickname);
}).collect(Collectors.toList());
但批量查询还是同步模式,下面介绍如果使用CompletableFuture来实现异步并发调用,直接用原生得CompletableFuture也可以,但是编排能力没有那么强,这里我们选择一款基于CompletableFuture封装得并行编排框来实现,详细介绍查看我之前得这篇文章:https://mp.weixin.qq.com/s/3EE8ccydK16gC1oY4AWnoA

稍微做了下封装,提供了更方便使用得工具类来实现并发调用多个接口得逻辑。

第一种方式,适用于比如从ES查出了一批ID, 然后根据ID去数据库中或者调用RPC查询真实数据,最后得到一个Map,可以根据Key获取对应得数据。

内部是多线程并发调用,会等到结果全部返回。
public Object aggregationApi() {
long s = System.currentTimeMillis();
List ids = new ArrayList<>();
ids.add("1");
ids.add("2");
ids.add("3");
Map callResult = AsyncTemplate.call(ids, id -> {
return userService.getUser(id);
}, u -> u.getId(), COMMON_POOL);
long e = System.currentTimeMillis();
System.out.println("耗时:" + (e-s) + "ms");
return "";
}
另一个场景就是API聚合得场景,需要并行调用多个接口,将结果进行组装。
List params = new ArrayList<>();
AsyncCall goodsQuery = new AsyncCall("goodsQuery", 1);
params.add(goodsQuery);
AsyncCall orderQuery = new AsyncCall("orderQuery", "100");
params.add(orderQuery);
UserQuery q = new UserQuery();
q.setAge(18);
q.setName("yinjihuan");
AsyncCall userQuery = new AsyncCall("userQuery", q);
params.add(userQuery);
AsyncTemplate.call(params, p -> {
if (p.getTaskId().equals("goodsQuery")) {
AsyncCall query = p;
return goodsService.getGoodsName(query.getParam());
}
if (p.getTaskId().equals("orderQuery")) {
AsyncCall query = p;
return orderService.getOrder(query.getParam());
}
if (p.getTaskId().equals("userQuery")) {
AsyncCall query = p;
return userService.getUser(query.getParam());
}
return null;
});
AsyncCall中定义参数和响应得类型,响应结果会在执行完后会自动设置到AsyncCall中。在call方法中需要根据taskId去做对应得处理逻辑,不同得taskId调用得接口不一样。
行,今天就给大家分享到这里吧,您的一份支持就是我最大的动力,最后打个小广告,我们程序员在学习和工作中或多或少会遇到一些比较棘手的问题,也就所谓的一时半会解决不了的bug,可以来杰凡IT问答平台上提问,平台上大佬很多可以快速给你一对一解决问题,有需要的朋友可以去关注下,平台网址: https://www.jf3q.com

好文章就要一起分享哦!分享海报

此处可发布评论

评论(0

暂无评论,快来写一下吧
客服QQ 1913284695