IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 批量查询接口如何巧妙利用单查询接口中的@Cacheable -> 正文阅读

[Java知识库]批量查询接口如何巧妙利用单查询接口中的@Cacheable

如标题所述,本文是为了探讨在已有jvm缓存的单查询接口的基础上增加批量查询接口功能,要如何实现,如何优化,如何抉择。

spring-cache用法请自行查询。

demo:

单查询接口如下:

@Service
public class BizCacheServiceImpl implements BizCacheService {

    /**
     * 单查询接口
     *
     * @param id
     * @return
     */
    @Cacheable(key = "'getBizCacheVO' + #id", value = "valueName")
    public BizCacheVO getStringById(Integer id) {
        //略去过程
        try {
            //模拟方法执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        BizCacheVO bizCacheVO = new BizCacheVO();
        bizCacheVO.setId(id);
        bizCacheVO.setValue("value");
        return bizCacheVO;
    }
}

一、单线程查询单个接口

  public Map<Integer, BizCacheVO> getListByIds(List<Integer> ids) {
        Map<Integer, BizCacheVO> result = new HashMap<>();
        for (Integer id : ids) {
            BizCacheVO bizCacheVO = bizCacheService.getStringById(id);
            if (bizCacheVO != null && bizCacheVO.getId() != null) {
                result.put(bizCacheVO.getId(), bizCacheVO);
            }
        }
        return result;
    }

缺点:如上图所述,单查询接口无缓存的情况下,执行时间大约为1s。如果同时请求20个,最起码也是20s。

优点:如果要查询的ids都被jvm缓存了,那完全没必要启用多线程,单线程完全可以适用,单线程还不浪费额外线程资源及上下文切换的损耗。

二、多线程查询单个接口

@Service
public class BizBatchCacheServiceImpl implements BizBatchCacheService {

    @Autowired
    private BizCacheService bizCacheService;
    //线程池
    private ExecutorService executor = Executors.newFixedThreadPool(50);

    /**
     * 批量接口
     *
     * @param ids
     * @return
     */
    public Map<Integer, BizCacheVO> getListByIds(List<Integer> ids) {
        Map<Integer, BizCacheVO> result = new HashMap<>();
        List<CompletableFuture<BizCacheVO>> completableFutureList = new ArrayList<>();
        for (Integer id : ids) {
            completableFutureList.add(CompletableFuture.supplyAsync(() -> bizCacheService.getStringById(id), executor));
        }
        try {
            CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{})).get();
            for (CompletableFuture<BizCacheVO> future : completableFutureList) {
                BizCacheVO bizCacheVO = future.get();
                if (bizCacheVO != null && bizCacheVO.getId() != null) {
                    result.put(bizCacheVO.getId(), bizCacheVO);
                }
            }
        } catch (Exception e) {
            //异常处理
        }
        return result;
    }
}

多线程如上述代码。

单查询接口无缓存的情况下,执行时间大约为1s。如果同时请求20个,总耗时也在1s多一些时间。看起来性能很不错,但有这么一个问题,批量接口的入参不固定,这一批数据里,可能有的已经存在缓存了,有的还没有缓存,那么有缓存的数据,还是额外的开启了线程去调用单查询接口,这里就造成了线程的消耗以及上下文切换时间的损耗。

缺点:已经缓存过的数据,还是会开启线程查询,消耗额外资源。

优点:如果数据大部分都是没有缓存的,这样调用的性能会远超单线程。

分析了单线程和多线程方式的调用,各有利弊,那怎么去平衡这两种方式呢?

那就是接下来的第三种方式。

三、cache+多线程接口

平衡点:让已经缓存过的直接去取缓存,未缓存的数据去开启多线程处理

@Service
public class BizBatchCacheServiceImpl implements BizBatchCacheService {

    @Autowired
    private BizCacheService bizCacheService;
    //线程池
    private ExecutorService executor = Executors.newFixedThreadPool(50);
    @Resource
    private CacheManager cacheManager;


    /**
     * cache+多线程方式批量接口
     *
     * @param ids
     * @return
     */
    public Map<Integer, BizCacheVO> getListByIds(List<Integer> ids) {
        Map<Integer, BizCacheVO> result = new HashMap<>();
        Cache cache = cacheManager.getCache("valueName");
        List<CompletableFuture<BizCacheVO>> completableFutureList = new ArrayList<>();
        Set<Integer> noHit = new HashSet<>();
        for (Integer id : ids) {
            BizCacheVO bizCacheVO = cache.get("getBizCacheVO" + id, BizCacheVO.class);
            if (bizCacheVO != null && bizCacheVO.getId() != null) {
                result.put(bizCacheVO.getId(), bizCacheVO);
            } else {
                noHit.add(id);
            }
        }
        //将未命中缓存的数据进行多线程请求
        for (Integer id : noHit) {
            completableFutureList.add(CompletableFuture.supplyAsync(() -> bizCacheService.getStringById(id), executor));
        }
        try {
            CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{})).get();
            for (CompletableFuture<BizCacheVO> future : completableFutureList) {
                BizCacheVO bizCacheVO = future.get();
                if (bizCacheVO != null && bizCacheVO.getId() != null) {
                    result.put(bizCacheVO.getId(), bizCacheVO);
                }
            }
        } catch (Exception e) {
            //异常处理
        }
        return result;
    }
}

优点:不再为已有的缓存数据浪费额外的线程资源,面对各种情况更灵活,更均衡

缺点:如果数据一直都有缓存,这样写就很鸡肋。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-01 15:33:33  更:2022-05-01 15:34:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:40:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码