如何扩展Spring Cache实现支持多级缓存

网友投稿 207 2023-02-20

如何扩展Spring Cache实现支持多级缓存

为什么多级缓存

缓存的引入是现在大部分系统所必须考虑的

redis 作为常用中间件,虽然我们一般业务系统(毕竟业务量有限)不会遇到如下图 在随着 data-size 的增大和数据结构的复杂的造成性能下降,但网络 IO 消耗会成为整个调用链路中不可忽视的部分。尤其在 微服务架构中,一次调用往往会涉及多次调用 例如pig oauth2.0 的 client 认证

Caffeine 来自未来的本地内存缓存,性能比如常见的内存缓存实现性能高出不少详细对比。

综合所述:我们需要构建 L1 Caffeine JVM 级别缓存 , L2 Redis 缓存。

设计难点

目前大部分应用缓存都是基于 Spring Cache 实现,基于注解(annotation)的缓存(cache)技术,存在的问题如下:

Spring Cache 仅支持 单一的缓存来源,即:只能选择 Redis 实现或者 Caffeine 实现,并不能同时使用。

数据一致性:各层缓存之间的数据一致性问题,如应用层缓存和分布式缓存之前的数据一致性问题。

缓存过期:Spring Cache 不支持主动的过期策略

业务流程

如何使用

引入依赖

com.pig4cloud.plugin

multilevel-cache-spring-boot-starter

0.0.1

开启缓存支持

@EnableCaching

public class App {

public static void main(String[] args) {

SpringApplication.run(App.class, args);

}

}

目标接口声明 Spring Cache 注解

@Cacheable(value = "get",key = "#key")

@GetMapping("/get")

public String get(String key){

return "success";

}

性能比较

为保证性能 redis 在 127.0.0.1 环路安装

OS: macOS Mojave

CPU: 2.3 GHz Intel Core i5

RAM: 8 GB 2133 MHz LPDDR3

JVM: corretto_11.jdk

Benchmark

Mode

Cnt

Score

Units

多级实现

thrpt

2

2716.074

ops/s

默认 redis

thrpt

2

1373.476

ops/s

代码原理

自定义 CacheManager 多级缓存实现

public class RedisCaffeineCacheManager implements CacheManager {

@Override

public Cache getCache(String name) {

Cache cache = cacheMap.get(name);

if (cache != null) {

return cache;

}

cache = new RedisCaffeineCache(name, stringKeyRedisTemplate, caffeineCache(), cacheConfigProperties);

Cache oldCache = cacheMap.putIfAbsent(name, cache);

log.debug("create cache instance, the cache name is : {}", name);

return oldCache == null ? cache : oldCache;

}

}

多级读取、过期策略实现

public class RedisCaffeineCache extends AbstractValueAdaptingCache {

protected Object lookup(Object key) {

Object cacheKey = getKey(key);

// 1. 先调用 caffeine 查询是否存在指定的值

Object value = caffeineCache.getIfPresent(key);

if (value != null) {

log.debug("get cache from caffeine, the key is : {}", cacheKey);

return value;

}

// 2. 调用 redis 查询在指定的值

value = stringKeyRedisTemplate.opsForValue().get(cacheKey);

if (value != null) {

log.debug("get cache from redis and put in caffeine, the key is : {}", cacheKey);

caffeineCache.put(key, value);

}

return value;

}

}

过期http://策略,所有更新操作都基于 redis pub/sub 消息机制更新

public class RedisCaffeineCache extends AbstractValueAdaptingCache {

@Override

public void put(Object key, Object value) {

push(new CacheMessage(this.name, key));

}

@Override

public ValueWrapper putIfAbsent(Object key, Object value) {

push(new CacheMessage(thisDOmbwJCncv.name, key));

}

@Override

public void evict(Object key) {

push(new CacheMessage(this.name, key));

}

@Override

public void clear() {

push(new CacheMessage(this.name, null));

}

private void push(CacheMessage message) {

stringKeyRedisTemplate.convertAndSend(topic, message);

}

}

MessageListener 删除指定 Caffeine 的指定值

public class CacheMessageListener implements MessageListener {

private final RedisTemplate redisTemplate;

private final RedisCaffeineCacheManager redisCaffeineCacheManager;

@Override

public void onMessage(Message message, byte[] pattern) {

CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());

cacheMessage.getCacheName(), cacheMessage.getKey());

redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey());

}

}

https://github.com/pig-mesh/multilevel-cache-spring-boot-starter

https://gitee.com/log4j/pig

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:api统一接口管理平台(统一api接口平台设计)
下一篇:云平台对于大数据开发影响(云平台对于大数据开发影响大吗)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~