一次首页功能的接口优化,真的是学到了,所有开发的同学一定会用到,收藏不迷路

102 阅读2分钟

33.png 我就以个人中心功能为例,说一下分组并发调度,个人中心一般会展示非常多的信息,比如用户信息,徽章,等级,粉丝数,关注数,获赞数,订单相关的数据等等,这些个业务模块的数据一般都由相关业务侧提供接口给前端使用,这种方式很明显是存在问题的,下面采用分组并发调度方式来聚合接口,只给前端提供一个封装了各个业务模块数据的接口,直接上代码

第一步 创建线程池

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;

}

结束,总结

  1. 如果需要调用10个接口组装数据,每个接口耗时500毫秒,串行调用下来至少5秒左右,这种多线程并行方式大概只需500毫秒左右,这里需要注意的是一次调用不要创建过多的Runnable任务,需要结合场景和线程池设置的参数来合理开发

  2. 一般一个公司的服务器都在一个机房里面,所以业务侧最好提供内网接口,请求耗时会更短

  3. 如果一个业务线接口调用出问题了,那么也不会影响到整理其他业务数据的展示,下图红色部分表示请求异常后会返回null,需提前做好处理逻辑

截屏2023-12-15 11.47.56.png

我在开发中在个人中心页面和售后详情页面就是这么用的,性能相当好

本代码请供参考