【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);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM