• guava本地缓存CacheLoader使用


    最近代码里看到有部分关于guava写本地缓存的,学习记录一下。

    首先guava的本地缓存可以理解成一个缓存map,以kv的形式存数据,不会持久化,没有支持分布式。比redis使用起来方便,不用引入额外的组件。如果是单机缓存的话,可以首先选择使用这种缓存方式。

    Guava cache的设计来源于CurrentHashMap是线程安全的,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。常见应用场景:对性能有非常高的要求、不经常变化、占用内存不大、有访问整个集合的需求、数据允许不时时一致。

    Guava cache的优点

    多种清理和淘汰策略
    支持并发(采用Segment做分区,线程安全)
    更新锁定(对同一个key,只让一个请求去读源并回填缓存,其他请求阻塞等待)
    集成数据源(一般我们在业务中操作缓存,都会操作缓存和数据源两部分GuavaCache的get可以集成数据源,在从缓存中读取不到时可以从数据源中读取数据并回填缓存)

    使用方法

    引入依赖

            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>30.1.1-jre</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    抽象类

    //引入数据库DAO
    
    //定义缓存的map格式
    public LoadingCache<Long,String> testCache
    
    //初始化
    @PostConstruct
    private void init(){
    	testCache = CacheBuilder.newBuilder()
    	 		// 初始大小
                .initialCapacity(1000)
    	 		// 缓存池大小
                .maximumSize(1000)
                // 设置时间对象没有被读/写访问则对象从内存中删除
                .expireAfterWrite(100, TimeUnit.MINUTES)
                //设置时间刷新缓存
                .refreshAfterWrite(60,TimeUnit.SECONDS)
                // 移除监听器
                .removalListener(
                    new RemovalListener<Long, String>() {
                      @Override
                      public void onRemoval(RemovalNotification<Long, String> rn) {
                        handleRemove.accept(rn.getKey());
                      }
                    })
                .recordStats()
                .build(
                    new CacheLoader<Long, String>() {
                    //第一次加载
                      @Override
                      public String load(Long aLong) throws Exception {
                        return handleNotExist.apply(aLong);
                      }
                      
                    //异步刷新(过期刷新机制,调用LoadingCache.refresh优先调用这里,未重写则调上面load)
                 	@Override
                 	public ListenableFuture<String> reload(Long aLong, String oldValue) throws Exception {
                 	ListenableFuture<String> task = ListenableFutureTask.creat( new Callable<String>(){
                 	public String call(){
                 	//请求数据库
                 		return querySqlDAO(key);
                 		}
                    });
                    refreshTaskExecutor.execute(task);
                    return task;
                    }
                   }
        log.info("初始化缓存");
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    实现类,直接继承抽象类后,写对应的query和refresh函数,参数判断,异常处理等。调用定义缓存LoadingCache的get/refresh方法,其中入参都是缓存的key,刷新会优先调用重写的reload去更新缓存,一般都重写为异步加载。如果reload没有被重写,则调用load走第一次加载的逻辑。

    使用的时候直接调用实现类中对应缓存的方法即可。

    参考:
    https://blog.csdn.net/ABestRookie/article/details/119901114

  • 相关阅读:
    简单dp刷题回忆录
    NX二次开发-VS使用NXOpen向导创建项目失败,再次弹出创建向导对话框,解决办法
    C语言的发展历史
    如何更好的使用Copilot
    Sentinel源码剖析之常用限流算法原理实现
    mysql慢查询日志
    mybatis 07: sql标签中 "#{}" 和 "${}" 的作用和比较
    【MySQL】DML
    OSI网络七层模型和TCP/IP模型
    《ClickHouse原理解析与应用实践》读书笔记(3)
  • 原文地址:https://blog.csdn.net/weixin_44139651/article/details/128184096