首页 > 其他分享 >自命为缓存之王的Caffeine(5)

自命为缓存之王的Caffeine(5)

时间:2023-02-15 21:47:15浏览次数:32  
标签:缓存 return String Caffeine value final 自命为 key public

您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~

 


 

 

普通的缓存和Token的区别在于时效性和持久性。如果用Redis实现Token的话,可以:

1、设置redis kv键值对的过期时间(秒数/毫秒数);

2、redis内部实现计时,无需代码干预,且有持久化;

3、kv超过指定过期时间即被自动删除。

自定义缓存计时非常麻烦,大部分中间件又没有过期失效。如果只是单节点,完全可以用Caffeine替代Redis。这只是一次有益的尝试,发现更多的可能性。

通过对缓存(而非Redis)功能的分析,可知几个关键点:

1、只要缓存失效即可,是否「过期」不是主要问题;

2、是否自动删除不重要,重要的是删除过期值,这个完全可以用代码实现;

3、既然自动计时用代码实现很麻烦,那么是不是可以换个思路呢?

想通了这几个问题,就可以通过变通的方式「曲线救国」,完全实现Redis的缓存功能。利用Mongo + Caffeine的方式,替代Redis的Token存储功能。

引入依赖与增加配置:

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-mongodb</artifactId>

        <exclusions>

            <exclusion>

                <groupId>com.lmax</groupId>

                <artifactId>disruptor</artifactId>

            </exclusion>

        </exclusions>

</dependency>

 

在配置文件中增加配置:

 

## MONGO

spring.data.mongodb.host=172.16.185.135

spring.data.mongodb.port=27017

spring.data.mongodb.database=0

spring.data.mongodb.username=test

spring.data.mongodb.password=123456

 

 

 

定义Mongo配置类:

/**
 * 去除_class字段的配置类
 *
 * @author 湘王
 */
@Configuration
public class MongoConfigure implements InitializingBean {
    @Resource
    private MappingMongoConverter mappingConverter;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 去除插入数据库的_class字段
        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
    }
}

 

 

定义Cache类:

/**
 * 缓存Document
 *
 * @author bear.xiong
 */
public class Cache implements Serializable {
    private static final long serialVersionUID = 7353685666928500768L;

    @Id
    private String id;
    private String key;
    private String value;
    private long time;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    @Override
    public String toString() {
        return String.format("{\"id\":\"%s\", \"key\":\"%s\", \"value\":\"%s\", \"time\":%d}", id, key, value, time);
    }
}

 

 

创建CacheDao类:

/**
 * 缓存Dao
 *
 * @author 湘王
 */
@Component
public class CacheDao<T> {
    @Autowired
    private MongoTemplate mongoTemplate;

    // expiretime指的是从存储到失效之间的时间间隔,单位毫秒
    @Cacheable(value = "test", key = "#key")
    public String getObject(final String key, final long expiretime) {
        Query query = new Query(Criteria.where("key").is(key));
        Cache cache = (Cache) mongoTemplate.findOne(query, Cache.class);
        System.out.println("getObject(" + key + ", " + expiretime + ") from mongo");

        if (null != cache) {
            // -1表示永不过期
            if (-1 == expiretime) {
                return cache.getValue();
            }
            // 如果当前时间 - 存储cache时的时间 >= 过期间隔
            long currentTtime = System.currentTimeMillis();
            if (currentTtime - cache.getTime() >= expiretime * 1000) {
                // 删除key,并返回null
                removeObject(key);
            } else {
                return cache.getValue();
            }
        }
        return null;
    }

    // 保存时,需要增加过期时间,方便同步到Caffeine
    @CachePut(value = "test", key = "#key")
    public boolean saveObject(final String key, final String value, final long expiretime) {
        Query query = new Query(Criteria.where("key").is(key));
        Update update = new Update();
        long time = System.currentTimeMillis();
        update.set("key", key);
        update.set("value", value);
        update.set("time", time);
        try {
            UpdateResult result = mongoTemplate.upsert(query, update, Cache.class);
            if (result.wasAcknowledged()) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @CacheEvict(value = "test", key = "#key")
    public boolean removeObject(final String key) {
        Query query = new Query(Criteria.where("key").is(key));
        try {
            DeleteResult result = mongoTemplate.remove(query, Cache.class);
            if (result.wasAcknowledged()) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}

 

 

创建CacheService类:

/**
 * 缓存Service接口
 *
 * @author 湘王
 */
@Service
public class CacheService {
   @Autowired
   private CacheDao<Cache> cacheDao;

   public String getObject(final String key, final long expiretime) {
      return cacheDao.getObject(key, expiretime);
   }

   public boolean saveObject(final String key, final String value, final long expiretime) {
      return cacheDao.saveObject(key, value, expiretime);
   }

   public boolean removeObject(final String key) {
      return cacheDao.removeObject(key);
   }
}

 

 

最后再创建CacheController类:

@RestController
public class CacheController {
    @Autowired
    private CacheService cacheService;

    @GetMapping("/cache/save")
    public void save(final String key, final String value, final int expiretime) {
        cacheService.saveObject(key, value, expiretime);
    }

    // 获取数据,过期时间为秒(会转换为毫秒)
    @GetMapping("/cache/get")
    public String get(final String key, final int expiretime) {
        String result = cacheService.getObject(key, expiretime);
        if (null == result) {
            return "expire value";
        }
        return result;
    }
}

 

测试后发现:先保存KV,再获取key,过期时间为3秒。但即使过了3秒,还是能获取到保存的数据,这是为什么呢?

 


 

 

感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

标签:缓存,return,String,Caffeine,value,final,自命为,key,public
From: https://www.cnblogs.com/xiangwang1111/p/17124798.html

相关文章

  • 在 Flask 中使用 Redis 来缓存数据
    (一)Redis简介Redis是一个高性能的key-value数据库,它是基于内存运行的数据库,因此有很高的性能,存取速度非常快,而且Redis还可以定期的将数据同步到磁盘中,实现数据的持......
  • 云音乐 iOS 跨端缓存库
    云音乐iOS跨端缓存库-NEMichelinCachehttps://mp.weixin.qq.com/s/jZ6QEuc0qoAn27lYzN1Yfw云音乐iOS跨端缓存库-NEMichelinCache原创 绎推 网易云音乐技术团......
  • 多级缓存降低高并发压力
    多级缓存简介1.传统缓存传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图:存在下面的问题:•由于redis的承受能力大于tomcat,所以请求要经......
  • 使用indexDB对接口请求进行简单的缓存数据处理
    exportfunction_getAction(url:string,param?:any,header?:any){returnnewPromise((resovle:any,error:any)=>{indexDBMap.getDataByKey(JSON......
  • Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、性能优化...
    Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、性能优化...Linux就该这么学 2023-02-1508:02 发表于北京作者:竹子爱熊猫  来源:juejin.cn/post/71......
  • Mybatis13 - 缓存介绍
    介绍理解缓存的工作机制和缓存的用途。1、缓存机制介绍2、一级缓存和二级缓存①使用顺序查询的顺序是:先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的......
  • Mybatis14 - 一级缓存
    一级缓存(默认开启)MyBatis的一级缓存是SqlSession级别的,即通过同一个SqlSession查询的数据会被缓存再次使用同一个SqlSession查询同一条数据,会从缓存中获取1、代码验证......
  • Mybatis15 - 二级缓存
    二级缓存(手动开启)这里我们使用的是Mybatis自带的二级缓存,也可以称之为是内置的。二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结......
  • Mybatis16 - 第三方缓存 EHCaChe
    1.EHCache简介官网地址:https://www.ehcache.org/Ehcacheisanopensource,standards-basedcachethatboostsperformance,offloadsyourdatabase,andsimplif......
  • Vue keep-alive缓存路由信息
    在不使用keep-alive时,通过路由跳转到另一组件上时,上一个组件会被vue销毁,在次进入,页面会保持初始状态,不会对用户的更改保留,如果需要包作在某组件上的更改,就可以使用keep-aliv......