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

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


普通的缓存和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秒,还是能获取到保存的数据,这是为什么呢?


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值