前言
使用springboot开发后台代码的时候,很核心的一个功能是为前端提供接口,那么很可能你会遇到如下问题:
1. 接口里面调用的service层是第三方库或者第三方后台程序,导致访问很慢。
2. 接口需要轮询,或者参数较多的情况下导致返回慢。
本文旨在解决如上的接口返回慢的问题,并给出解决方案与思路。
一、使用Callable+FutureTask 实现多线程并发的方式
该思路是很容易想到的一种可行性方案,因为多线程可以大大提高后台处理速度,而且该方式是JAVA自带的。
思路:
1. 开N个线程
2. 引用callable 封装线程需要执行的task
3. 线程里面调用task,并执行。
4. 收集各线程的结果并返回
@Servicepublic classFutureTaskByReq {public static List>multiTaskGetReq(String projectid, String versionid) {//开启多线程
ExecutorService exs = Executors.newFixedThreadPool(10);List> retList = Collections.synchronizedList(newArrayList());try{//结果集//List list = new ArrayList();
List>> futureList = new ArrayList>>();//启动线程池,10个任务固定线程数为5
for (int i = 0; i < version.length; i++) {
FutureTask> futureTask = new FutureTask>(newCallableTask(projectid, versionid));//提交任务,添加返回,Runnable特性
exs.submit(futureTask);//Future特性
futureList.add(futureTask);
}//结果归集
while (futureList.size() > 0) {
Iterator>> iterable =futureList.iterator();//遍历一遍
while(iterable.hasNext()) {
Future> 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();
}returnretList;
}/*** @Description 回调方法*/
public static class CallableTask implements Callable>{
String projectid;
String versionid;publicCallableTask(String projectid, String versionid) {super();this.projectid =projectid;this.versionid =versionid;
}
@Overridepublic Mapcall() {
Map retmap=new HashMap<>();//你想要执行的task
returnretmap;
}
}
二、使用定时任务+缓存的方式解决接口返回慢的问题
思路与场景: 有些接口的访问量不大,或者请求的数据变动不频繁,可以暂时存放到缓存,例如redis里面。那么获取的时候就是即时获取。
springboot自带了 @EnableScheduling 注解,可以执行定时任务。采用cron 表达式即可精准定时执行。
Redis 则有大量的缓存策略与算法,这里推荐使用LRU算法进行存取都很方便。
定时部分:
@Component
@Configuration//1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling //2.开启定时任务
public classLocalSchedule {//添加定时任务,每天12点定时执行
@Scheduled(cron = "0 0 12 * * ?")//或直接指定时间间隔,例如:5秒//@Scheduled(fixedRate=5000)
private voidconfigureTasks() {//TODO 需要执行的定时任务,主要是比较慢的接口
System.err.println("执行静态1定时任务时间: " +LocalDateTime.now());
}
}
LRU算法:
/*** 使用LRU策略进行一些数据缓存。*/
public classLRULocalCache {/*** 默认有效时长,单位:秒*/
private static final long DEFUALT_TIMEOUT =;private static final Mapmap;private static finalTimer timer;/*** 初始化*/
static{
timer= newTimer();//map = new LRUMap<>();
map = new ConcurrentHashMap<>();
}/*** 私有构造函数,工具类不允许实例化*/
privateLRULocalCache() {
}/*** 基于LRU策略的map
*
*@param
*@param*/
static class LRUMap extends LinkedHashMap{/*** 读写锁*/
private final ReadWriteLock readWriteLock = newReentrantReadWriteLock();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;publicLRUMap() {super(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}public LRUMap(intinitialCapacity) {super(initialCapacity, DEFAULT_LOAD_FACTOR);
}public voidclear() {
wLock.lock();try{super.clear();
}finally{
wLock.unlock();
}
}/*** 重写LinkedHashMap中removeEldestEntry方法;
* 新增元素的时候,会判断当前map大小是否超过DEFAULT_MAX_CAPACITY,超过则移除map中最老的节点;
*
*@parameldest
*@return
*/
protected boolean removeEldestEntry(Map.Entryeldest) {return size() >DEFAULT_MAX_CAPACITY;
}
}/*** 清除缓存任务类*/
static class CleanWorkerTask extendsTimerTask {privateString key;publicCleanWorkerTask(String key) {this.key =key;
}public voidrun() {
map.remove(key);
}
}/*** 增加缓存
*
*@paramkey
*@paramvalue*/
public static voidadd(String key, Object value) {
map.put(key, value);
timer.schedule(newCleanWorkerTask(key), DEFUALT_TIMEOUT);
}/*** 增加缓存
*
*@paramkey
*@paramvalue
*@paramtimeout 有效时长*/
public static void put(String key, Object value, inttimeout) {
map.put(key, value);
timer.schedule(new CleanWorkerTask(key), timeout *SECOND_TIME);
}/*** 增加缓存
*
*@paramkey
*@paramvalue
*@paramexpireTime 过期时间*/
public static voidput(String key, Object value, Date expireTime) {
map.put(key, value);
timer.schedule(newCleanWorkerTask(key), expireTime);
}/*** 获取缓存
*
*@paramkey
*@return
*/
public staticObject get(String key) {returnmap.get(key);
}
}