前言
使用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); } }