前言

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

【SpringBoot】 一种解决接口返回慢的方式的更多相关文章

  1. Ext_两种处理服务器端返回值的方式

    1.Form表单提交返回值处理 //提交基本信息表单  f.form.submit({      clientValidation:true,      //表单提交后台处理地址      url:' ...

  2. python3乱码问题:接口返回数据中文乱码问题解决

    昨天测试接口出现有一个接口中文乱码问题,现象: 1 浏览器请求返回显示正常 2 用代码请求接口返回数据中文显示乱码 3 使用的python3,python3默认unicode编码,中文都是可以正常显示 ...

  3. C#调用接口返回json数据中含有双引号 或其他非法字符的解决办法

    这几天,调用别人接口返回json数据含有特殊符号(双引号),当转换成json对象总是报错, json字符格式如下 { "BOXINFO":[ { ", "ITE ...

  4. 两种解决springboot 跨域问题的方法示例

    两种解决springboot 跨域问题的方法示例,哪种方法看情况而定,自己选择.社会Boolean哥,人狠话不多,直接上代码. 第一种实现方式:       此种方式做全局配置,用起来更方便,但是无法 ...

  5. WORD 的 Open 和Workbook 的 LoadFromFile 函数返回null的一种解决方法

    WORD Application.Documents.Open 和 Workbook workbookExcel.LoadFromFile 函数返回null的一种解决方法 DCOM Config Se ...

  6. JavaScript监听手机物理返回键的两种解决方法

    JavaScript没有监听物理返回键的API,所以只能使用 popstate 事件监听. 有两个解决办法: 1.返回到指定的页面 pushHistory(); window.addEventList ...

  7. SpringBoot系列之前后端接口安全技术JWT

    @ 目录 1. 什么是JWT? 2. JWT令牌结构怎么样? 2.1 标头(Header) 2.2 有效载荷(Playload) 2.3 签名(Signature) 3. JWT原理简单介绍 4. J ...

  8. C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解

    前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解  ...

  9. TIME_WAIT 另一种解决方式 SO_LINGER

         被TIME_WAIT问题烦得不行,又发现了另一种解决方式,SO_LINGER.      对SO_LINGER解释最通用的自然是<UNP>,现摘录如下      SO_LINGE ...

随机推荐

  1. STL之pair及其非成员函数make_pair()

    std::pair是一个结构模板,提供了一种将两个异构对象存储为一个单元的方法. 定义于头文件 <utility> template< class T1, class T2 > ...

  2. httpclient模拟服务器请求

    // 创建默认的httpClient实例. CloseableHttpClient httpclient = HttpClients.createDefault(); // 创建httppost Ht ...

  3. C++中使用CMake编译管理项目

    CMake是一个跨平台的Makefile生成工具,可以根据特定的规则生成相应的Makefile文件,并对C/C++源代码进行编译和管理. 有一篇博客介绍CMake的使用,比较通俗易懂,链接地址是:Cm ...

  4. Jmeter录制web和app脚本

    前置: 一.已经安装了jmeter 步骤: 一.打开jmeter,右键测试计划-->添加添加线程组-->线程-->线程组,新建线程组 二.右键线程组-->添加-->逻辑控 ...

  5. 洛谷 P1440 求m区间内的最小值(单调队列)

    题目链接 https://www.luogu.org/problemnew/show/P1440 显然是一道单调队列题目…… 解题思路 对于单调队列不明白的请看这一篇博客:https://www.cn ...

  6. 第二章、Python基础

    @编程语言分类: a.从运行角度分: 编译型:代码本身不能运行,需要一个附加程序(编译器〉将其转换成由二进制代码组成的可执行文件,然后才可以运行. 如 c/c++ 特点: 开发效率低.执行速度快 解释 ...

  7. MapReduce数据格式化------<一>

    引言: 我们知道:在MapReduce程序的Map阶段,需要有数据输入,而由于数据往往大小不规则,所以在数据输入Mapper之前,需要根据数据的特点和业务逻辑对数据进行格式化.这一步的格式化被称为:I ...

  8. NGUI的sprite的使用(九宫切图)

    一,图集的添加和背景图的选择 图集的选择Atlas和背景图的选择Sprite 二,type的sliced切背景图的使用 当选择Sprite后的edit时,我们就可以设置当前背景图的边距,也有切图的意思 ...

  9. Git配置全局账号密码避免每次拉取、提交输入账号密码

    前言 在大家使用github的过程中,一定会碰到这样一种情况,就是每次要push 和pull时总是要输入github的账号和密码,这样不仅浪费了大量的时间且降低了工作效率.在此背景下,本文在网上找了两 ...

  10. zabbix3.4.8配置自动发现主机并监控

    一.       自动发现功能简介 Zabbix服务器端通过网络或者主机名等方式进行客户端的扫描发现,从进行加入到监控的主机队列中,适用于批量加入多主机监控的场景. 二.       自动发现功能实施 ...