Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

spring cache 多级缓存扩展

License

NotificationsYou must be signed in to change notification settings

pig-mesh/multilevel-cache-spring-boot-starter

Repository files navigation

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

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

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

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

设计难点

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

  • Spring Cache 仅支持 单一的缓存来源,即:只能选择 Redis 实现或者 Caffeine 实现,并不能同时使用。
  • 数据一致性:各层缓存之间的数据一致性问题,如应用层缓存和分布式缓存之前的数据一致性问题。
  • 缓存过期:Spring Cache 不支持主动的过期策略

业务流程

如何使用

版本支持
3.0.0适配 SpringBoot3.x
1.0.1适配 SpringBoot2.x
    1. 引入依赖
<dependency>    <groupId>com.pig4cloud.plugin</groupId>    <artifactId>multilevel-cache-spring-boot-starter</artifactId>    <version>${lastVersion}</version></dependency>
    1. 开启缓存支持
@EnableCachingpublicclassApp {publicstaticvoidmain(String[]args) {SpringApplication.run(App.class,args);}}
    1. 目标接口声明 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
BenchmarkModeCntScoreUnits
多级实现thrpt22716.074ops/s
默认 redisthrpt21373.476ops/s

代码原理

    1. 自定义 CacheManager 多级缓存实现
publicclassRedisCaffeineCacheManagerimplementsCacheManager {@OverridepublicCachegetCache(Stringname) {Cachecache =cacheMap.get(name);if (cache !=null) {returncache;}cache =newRedisCaffeineCache(name,stringKeyRedisTemplate,caffeineCache(),cacheConfigProperties);CacheoldCache =cacheMap.putIfAbsent(name,cache);log.debug("create cache instance, the cache name is : {}",name);returnoldCache ==null ?cache :oldCache;}}
    1. 多级读取、过期策略实现
publicclassRedisCaffeineCacheextendsAbstractValueAdaptingCache {protectedObjectlookup(Objectkey) {ObjectcacheKey =getKey(key);// 1. 先调用 caffeine 查询是否存在指定的值Objectvalue =caffeineCache.getIfPresent(key);if (value !=null) {log.debug("get cache from caffeine, the key is : {}",cacheKey);returnvalue;}// 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);}returnvalue;}}
    1. 过期策略,所有更新操作都基于 redis pub/sub 消息机制更新
publicclassRedisCaffeineCacheextendsAbstractValueAdaptingCache {@Overridepublicvoidput(Objectkey,Objectvalue) {push(newCacheMessage(this.name,key));}@OverridepublicValueWrapperputIfAbsent(Objectkey,Objectvalue) {push(newCacheMessage(this.name,key));}@Overridepublicvoidevict(Objectkey) {push(newCacheMessage(this.name,key));}@Overridepublicvoidclear() {push(newCacheMessage(this.name,null));}privatevoidpush(CacheMessagemessage) {stringKeyRedisTemplate.convertAndSend(topic,message);}}
    1. MessageListener 删除指定 Caffeine 的指定值
publicclassCacheMessageListenerimplementsMessageListener {privatefinalRedisTemplate<Object,Object>redisTemplate;privatefinalRedisCaffeineCacheManagerredisCaffeineCacheManager;@OverridepublicvoidonMessage(Messagemessage,byte[]pattern) {CacheMessagecacheMessage = (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


[8]ページ先頭

©2009-2025 Movatter.jp