zoukankan      html  css  js  c++  java
  • 【SpringBoot】 一种解决接口返回慢的方式

    前言

    使用springboot开发后台代码的时候,很核心的一个功能是为前端提供接口,那么很可能你会遇到如下问题:

    1. 接口里面调用的service层是第三方库或者第三方后台程序,导致访问很慢。

    2. 接口需要轮询,或者参数较多的情况下导致返回慢。

    本文旨在解决如上的接口返回慢的问题,并给出解决方案与思路。

    一、使用Callable+FutureTask 实现多线程并发的方式

    该思路是很容易想到的一种可行性方案,因为多线程可以大大提高后台处理速度,而且该方式是JAVA自带的。

    思路: 

    1. 开N个线程

    2. 引用callable 封装线程需要执行的task

    3. 线程里面调用task,并执行。 

    4. 收集各线程的结果并返回

    @Service
    public class FutureTaskByReq {public static List<Map<String,Integer>> multiTaskGetReq(String projectid, String versionid) {
            //开启多线程
            ExecutorService exs = Executors.newFixedThreadPool(10);
            List<Map<String,Integer>> retList = Collections.synchronizedList(new ArrayList());
            try {
                //结果集
    //            List<Integer> list = new ArrayList<Integer>();
                List<FutureTask<Map<String,Integer>>> futureList = new ArrayList<FutureTask<Map<String,Integer>>>();
                //启动线程池,10个任务固定线程数为5
                for (int i = 0; i < version.length; i++) {
                    FutureTask<Map<String,Integer>> futureTask = new FutureTask<Map<String,Integer>>(new CallableTask(projectid, versionid));
                    //提交任务,添加返回,Runnable特性
                    exs.submit(futureTask);
                    //Future特性
                    futureList.add(futureTask);
                }
                //结果归集
                while (futureList.size() > 0) {
                    Iterator<FutureTask<Map<String,Integer>>> iterable = futureList.iterator();
                    //遍历一遍
                    while (iterable.hasNext()) {
                        Future<Map<String,Integer>> future = iterable.next();
                        if (future.isDone() && !future.isCancelled()) {
                            //Future特性
                            retList.add(future.get());
                            //任务完成移除任务
                            iterable.remove();
                        } else {
                            //避免CPU高速轮循,可以休息一下。
                            Thread.sleep(1);
                        }
                    }
                }
    
    //            System.out.println("list=" + retList);
    //            System.out.println("总耗时=" + (System.currentTimeMillis() - start) + ",取结果归集耗时=" + (System.currentTimeMillis() - getResultStart));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                exs.shutdown();
            }return retList;
        }
    
    
        /**
         * @Description 回调方法
         */
        public static class CallableTask implements Callable<Map<String,Integer>> {
            String projectid;
            String versionid;
    
            public CallableTask(String projectid, String versionid) {
                super();
                this.projectid = projectid;
                this.versionid = versionid;
            }
    
            @Override
            public Map<String,Integer> call() {
                Map<String,Integer> retmap=new HashMap<>();
                //你想要执行的task
                return retmap;
            }
    
        }

    二、使用定时任务+缓存的方式解决接口返回慢的问题

    思路与场景: 有些接口的访问量不大,或者请求的数据变动不频繁,可以暂时存放到缓存,例如redis里面。那么获取的时候就是即时获取。

    springboot自带了 @EnableScheduling 注解,可以执行定时任务。采用cron 表达式即可精准定时执行。

    Redis 则有大量的缓存策略与算法,这里推荐使用LRU算法进行存取都很方便。

    定时部分:

    @Component
    @Configuration      //1.主要用于标记配置类,兼备Component的效果。
    @EnableScheduling   // 2.开启定时任务
    public class LocalSchedule {
    
        //添加定时任务,每天12点定时执行
        @Scheduled(cron = "0 0 12 * * ?" )
        //或直接指定时间间隔,例如:5秒
        //@Scheduled(fixedRate=5000)
        private void configureTasks() {
            //TODO 需要执行的定时任务,主要是比较慢的接口
            
            System.err.println("执行静态1定时任务时间: " + LocalDateTime.now());
        }
    }

    LRU算法:

    /**
     * 使用LRU策略进行一些数据缓存。
     */
    public class LRULocalCache {
    
        /**
         * 默认有效时长,单位:秒
         */
        private static final long DEFUALT_TIMEOUT = ;private static final Map<String, Object> map;
    
        private static final Timer timer;
    
    
    
        /**
         * 初始化
         */
        static {
            timer = new Timer();
    //        map = new LRUMap<>();
            map = new ConcurrentHashMap<>();
        }
    
    
        /**
         * 私有构造函数,工具类不允许实例化
         */
        private LRULocalCache() {
    
        }
    
        /**
         * 基于LRU策略的map
         *
         * @param <K>
         * @param <V>
         */
        static class LRUMap<K, V> extends LinkedHashMap<K, V> {
    
            /**
             * 读写锁
             */
            private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
            private final Lock rLock = readWriteLock.readLock();
    
            private final Lock wLock = readWriteLock.writeLock();
    
            /**
             * 默认缓存容量
             */
            private static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
    
            /**
             * 默认最大缓存容量
             */
    //        private static final int DEFAULT_MAX_CAPACITY = 1 << 30;
            private static final int DEFAULT_MAX_CAPACITY = 1 << 18;
    
            /**
             * 加载因子
             */
            private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
            public LRUMap() {
                super(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
            }
    
            public LRUMap(int initialCapacity) {
                super(initialCapacity, DEFAULT_LOAD_FACTOR);
            }
    
    
            public void clear() {
                wLock.lock();
                try {
                    super.clear();
                } finally {
                    wLock.unlock();
                }
            }
    
    
            /**
             * 重写LinkedHashMap中removeEldestEntry方法;
             * 新增元素的时候,会判断当前map大小是否超过DEFAULT_MAX_CAPACITY,超过则移除map中最老的节点;
             *
             * @param eldest
             * @return
             */
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                return size() > DEFAULT_MAX_CAPACITY;
            }
    
        }
    
        /**
         * 清除缓存任务类
         */
        static class CleanWorkerTask extends TimerTask {
    
            private String key;
    
            public CleanWorkerTask(String key) {
                this.key = key;
            }
    
            public void run() {
                map.remove(key);
            }
        }
    
        /**
         * 增加缓存
         *
         * @param key
         * @param value
         */
        public static void add(String key, Object value) {
            map.put(key, value);
            timer.schedule(new CleanWorkerTask(key), DEFUALT_TIMEOUT);
    
        }
    
        /**
         * 增加缓存
         *
         * @param key
         * @param value
         * @param timeout 有效时长
         */
        public static void put(String key, Object value, int timeout) {
            map.put(key, value);
            timer.schedule(new CleanWorkerTask(key), timeout * SECOND_TIME);
        }
    
        /**
         * 增加缓存
         *
         * @param key
         * @param value
         * @param expireTime 过期时间
         */
        public static void put(String key, Object value, Date expireTime) {
            map.put(key, value);
            timer.schedule(new CleanWorkerTask(key), expireTime);
        }
    /**
         * 获取缓存
         *
         * @param key
         * @return
         */
        public static Object get(String key) {
            return map.get(key);
        }
    }
  • 相关阅读:
    IFS二次开发03——Item
    TFS二次开发01——TeamProjectsPicher
    我的八年程序之路(二)三月方便面换来800月薪
    我的八年程序之路(一)求职的艰辛
    重温CSS之背景、文本样式
    重温CSS之文档结构
    重温CSS之基础
    不使用第三方软件、使用IE11自带功能来屏蔽浏览器广告
    Windwos8.1下配置PHP环境
    Python学习系列之(二)图解Windows8.1下安装Django
  • 原文地址:https://www.cnblogs.com/Ronaldo-HD/p/11510681.html
Copyright © 2011-2022 走看看