我就以个人中心功能为例,说一下分组并发调度,个人中心一般会展示非常多的信息,比如用户信息,徽章,等级,粉丝数,关注数,获赞数,订单相关的数据等等,这些个业务模块的数据一般都由相关业务侧提供接口给前端使用,这种方式很明显是存在问题的,下面采用分组并发调度方式来聚合接口,只给前端提供一个封装了各个业务模块数据的接口,直接上代码
第一步 创建线程池
ExecutorService executor = new ThreadPoolExecutor(64, 64,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("async-pool-%d").setUncaughtExceptionHandler((t, e) -> logger.error("thread error, name: " + t.getName(), e)).build(),
new ThreadPoolExecutor.AbortPolicy());
第二步 使用线程池执行多个任务,这里涉及到CompletableFuture,如果不了解的需要自行找资料学习一下
public void allOfTask(List<Runnable> tasks, long timeout, TimeUnit unit) {
List<CompletableFuture<Void>> list = tasks.stream()
.map(runnable -> CompletableFuture.runAsync(runnable, executor)
.exceptionally(throwable -> {
logger.error("", throwable);
return null;
})
)
.collect(Collectors.toList());
CompletableFuture<Void> all = CompletableFuture.allOf(list.toArray(new CompletableFuture[0]));
try {
all.get(timeout, unit);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
logger.info("all of task error. e: {}, message: {}", e.getClass().getName(), e.getMessage());
}
List<CompletableFuture<Void>> r = list.stream().filter(future -> !future.isDone()).collect(Collectors.toList());
logger.info("all of task result. task count: {}, fail count: {}", tasks.size(), r.size());
}
第三步 将个人首页中各个业务侧接口进行整合处理
public UserInfoResp getUserInfo(long userId) {
UserInfoResp resp = new UserInfoResp();
Runnable task1 = () -> {
//调用徽章接口,获取徽章,将结果set到返回值中
resp.set.....
};
Runnable task2 = () -> {
//调用用户信息接口,获取用户头像、昵称等数据,将结果set到返回值中
resp.set.....
};
Runnable task3 = () -> {
// 不用我多说了吧.....
};
List<Runnable> taskList = Lists.newArrayList(task1,task2,task3);
//调用上面allOfTask方法即可
allOfTask(taskList, 200, TimeUnit.MILLISECONDS);
return resp;
}
结束,总结
-
如果需要调用10个接口组装数据,每个接口耗时500毫秒,串行调用下来至少5秒左右,这种多线程并行方式大概只需500毫秒左右,这里需要注意的是一次调用不要创建过多的Runnable任务,需要结合场景和线程池设置的参数来合理开发
-
一般一个公司的服务器都在一个机房里面,所以业务侧最好提供内网接口,请求耗时会更短
-
如果一个业务线接口调用出问题了,那么也不会影响到整理其他业务数据的展示,下图红色部分表示请求异常后会返回null,需提前做好处理逻辑
我在开发中在个人中心页面和售后详情页面就是这么用的,性能相当好
本代码请供参考