美文网首页
接口异步调用,接口耗时减少的可不是一点点

接口异步调用,接口耗时减少的可不是一点点

作者: 红兔哥 | 来源:发表于2021-05-23 22:40 被阅读0次

    随着业务发展,底层数据量越来越大,业务逻辑也日趋复杂化,某些接口耗时也越来越长,这时候接口就需要进行性能优化了,当然性能优化主要跟业务相关涉及改造点可能各不相同,这里就来介绍异步调用多个接口减少响应时间

    适用条件

    • 调用多个独立的接口,接口间无相互依赖关系
    • 非耗时最大的接口占总耗时比重较大

    优化前调用方式

    优化前的代码按照顺序调用方式:

    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class DemoTest {
    
        public static void main(String[] args) throws Exception {
            long beginTime = System.currentTimeMillis();
            int processA = new InterfaceA().process();
            int processB = new InterfaceB().process();
            int result = processA + processB;
            log.info("执行结果:{} 耗时:{}", result, System.currentTimeMillis() - beginTime);
        }
    
        @Slf4j
        public final static class InterfaceA {
            Integer result = 1;
    
            public int process() {
                long beginTime = System.currentTimeMillis();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("InterfaceA.process Exception");
                }
                log.info("执行接口InterfaceA.process 耗时:{}ms", System.currentTimeMillis() - beginTime);
                return result;
            }
        }
    
        @Slf4j
        public final static class InterfaceB {
            Integer result = 1;
    
            public int process() {
                long beginTime = System.currentTimeMillis();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("InterfaceB.process Exception");
                }
                log.info("执行接口InterfaceB.process 耗时:{}ms", System.currentTimeMillis() - beginTime);
                return result;
            }
        }
    }
    

    执行结果:

    21:40:17.603 [main] INFO DemoTest$InterfaceA - 执行接口InterfaceA.process 耗时:2002ms
    21:40:19.612 [main] INFO DemoTest$InterfaceB - 执行接口InterfaceB.process 耗时:2001ms
    21:40:19.613 [main] INFO DemoTest - 执行结果:2 耗时:4018
    

    优化后调用方式

    优化后的代码按照异步调用方式:

    import cn.hutool.core.thread.ThreadFactoryBuilder;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.Future;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    @Slf4j
    public class DemoTest {
        private static ThreadPoolExecutor pool = new ThreadPoolExecutor(
                5,
                5,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(1000),
                ThreadFactoryBuilder.create().setNamePrefix("线程名称-").build()
        );
    
        public static void main(String[] args) throws Exception {
            long beginTime = System.currentTimeMillis();
    
            List<Future<Integer>> futures = new ArrayList<>(2);
            List<Integer> results = new ArrayList<>(2);
            futures.add(pool.submit(() -> new InterfaceA().process()));
            futures.add(pool.submit(() -> new InterfaceB().process()));
            for (Future<Integer> item : futures) {
                results.add(item.get());
            }
            
            int result = results.get(0) + results.get(1);
            log.info("执行结果:{} 耗时:{}", result, System.currentTimeMillis() - beginTime);
        }
    
        @Slf4j
        public final static class InterfaceA {
            Integer result = 1;
    
            public int process() {
                long beginTime = System.currentTimeMillis();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("InterfaceA.process Exception");
                }
                log.info("执行接口InterfaceA.process 耗时:{}ms", System.currentTimeMillis() - beginTime);
                return result;
            }
        }
    
        @Slf4j
        public final static class InterfaceB {
            Integer result = 1;
    
            public int process() {
                long beginTime = System.currentTimeMillis();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("InterfaceB.process Exception");
                }
                log.info("执行接口InterfaceB.process 耗时:{}ms", System.currentTimeMillis() - beginTime);
                return result;
            }
        }
    }
    

    执行结果:

    22:03:43.180 [线程名称-1] INFO DemoTest$InterfaceB - 执行接口InterfaceB.process 耗时:2004ms
    22:03:43.180 [线程名称-0] INFO DemoTest$InterfaceA - 执行接口InterfaceA.process 耗时:2004ms
    22:03:43.190 [main] INFO DemoTest - 执行结果:2 耗时:2020
    

    此方式还可以结合CompletionService可实现异步任务和执行结果分离,大家可以自行搜索实践

    强大的CompletableFuture JDK1.8

    import com.google.common.collect.Lists;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CompletableFuture;
    
    @Slf4j
    public class DemoTest {
    
        public static void main(String[] args) throws Exception {
            long beginTime = System.currentTimeMillis();
    
            CompletableFuture<Integer> interfaceFuturesA = CompletableFuture.supplyAsync(() -> new InterfaceA().process());
            CompletableFuture<Integer> interfaceFuturesB = CompletableFuture.supplyAsync(() -> new InterfaceB().process());
            CompletableFuture<List<Integer>> future = CompletableFuture
                    .allOf(interfaceFuturesA, interfaceFuturesB)
                    .thenApply((none) -> {
                        List<Integer> dataList = new ArrayList<>(2);
                        try {
                            dataList.add(interfaceFuturesA.get());
                            dataList.add(interfaceFuturesB.get());
                        } catch (Exception e) {
                            log.error("执行异常");
                        }
                        return dataList;
                    }).exceptionally(e -> Lists.newArrayList());
    
            int result = future.get().get(0) + future.get().get(1);
            log.info("执行结果:{} 耗时:{}", result, System.currentTimeMillis() - beginTime);
        }
    
        @Slf4j
        public final static class InterfaceA {
            Integer result = 1;
    
            public int process() {
                long beginTime = System.currentTimeMillis();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("InterfaceA.process Exception");
                }
                log.info("执行接口InterfaceA.process 耗时:{}ms", System.currentTimeMillis() - beginTime);
                return result;
            }
        }
    
        @Slf4j
        public final static class InterfaceB {
            Integer result = 1;
    
            public int process() {
                long beginTime = System.currentTimeMillis();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    log.error("InterfaceB.process Exception");
                }
                log.info("执行接口InterfaceB.process 耗时:{}ms", System.currentTimeMillis() - beginTime);
                return result;
            }
        }
    }
    

    执行结果:

    22:31:44.822 [ForkJoinPool.commonPool-worker-5] INFO DemoTest$InterfaceB - 执行接口InterfaceB.process 耗时:2005ms
    22:31:44.822 [ForkJoinPool.commonPool-worker-3] INFO DemoTest$InterfaceA - 执行接口InterfaceA.process 耗时:2002ms
    22:31:44.831 [main] INFO DemoTest - 执行结果:2 耗时:2027
    

    优化时注意点

    • 使用线程池防止内存溢出风险
    • 执行结果容器可自行根据需要设置
    • 接口粒度可根据实际业务情况组合和拆分

    相关文章

      网友评论

          本文标题:接口异步调用,接口耗时减少的可不是一点点

          本文链接:https://www.haomeiwen.com/subject/bqktsltx.html