缓存管理器CacheManager

一、背景

​ 代码并发量因建行活动页上升,大量请求打到Mongo导致数据库cpu100%从而服务不可用,目前解决方案,使用编程式缓存,即对缓存的操作与业务代码耦合。目前基本上可以解决并发问题。此次提出CacheManager主要是优化代码。使用声明式,即注解的方式,灵活操纵缓存,不需要与业务代码耦合。

二、与Springboot2集成

1、引入依赖

<!--SpringCache的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2、SpringCache配置

/**
* @Title: CacheManagerConfiguration
* @author: simon
* @date: 2021/12/27 14:14
*/
@EnableCaching
@Configuration
public class CacheManagerConfiguration extends CachingConfigurerSupport { private static String SEPARATOR = "&"; @Autowired
ReactiveMongoTemplate reactiveMongoTemplate; /**
* 缓存管理器
*
* @param lettuceConnectionFactory
* @return
* @author simon
*/
@Bean("redisCacheManager")
@Primary
public CacheManager cacheManager(@Qualifier("RedisConnectionFactory")
LettuceConnectionFactory lettuceConnectionFactory)
{
RedisSerializer<String> redisSerializer = new StringRedisSerializer(); // 解决查询缓存转换异常的问题
ObjectMapper mapper = new ObjectMapper(); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); //反序列化时候遇到不匹配的属性并不抛出异常
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//序列化时候遇到空对象不抛出异常
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//反序列化的时候如果是无效子类型,不抛出异常
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
//不使用默认的dateTime进行序列化,
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
//使用JSR310提供的序列化类,里面包含了大量的JDK8时间序列化类
mapper.registerModule(new JavaTimeModule());
//启用反序列化所需的类型信息,在属性中添加@class
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer(mapper);
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer); // 初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory);
// 默认缓存配置
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.computePrefixWith(comPrefix -> RedisConstants.KEY_BASE_PRIMITIVE.concat(RedisConstants.KEY_SEPARATOR)
.concat("cacheManager")
.concat(RedisConstants.KEY_SEPARATOR)
.concat(comPrefix)
.concat(RedisConstants.KEY_SEPARATOR))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(pair);
// 初始化缓存key
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(16);
configurationMap.put(CacheDurationConstant.FOREVER_CACHE, defaultCacheConfig);
configurationMap.put(CacheDurationConstant.THREE_DAYS_CACHE, defaultCacheConfig.entryTtl(Duration.ofDays(3)));
configurationMap.put(CacheDurationConstant.ONE_DAYS_CACHE, defaultCacheConfig.entryTtl(Duration.ofDays(1)));
configurationMap.put(CacheDurationConstant.ONE_HOURS_CACHE, defaultCacheConfig.entryTtl(Duration.ofHours(1)));
configurationMap.put(CacheDurationConstant.TWO_MINUTES_CACHE, defaultCacheConfig.entryTtl(Duration.ofMinutes(2)));
configurationMap.put(CacheDurationConstant.FIVE_SECONDS_CACHE, defaultCacheConfig.entryTtl(Duration.ofSeconds(5))); return RedisCacheManager.builder(redisCacheWriter)
// 初始化缓存空间
.initialCacheNames(configurationMap.keySet())
// 初始化缓存配置
.withInitialCacheConfigurations(configurationMap)
// 默认缓存配置
.cacheDefaults(defaultCacheConfig)
.build();
} @Bean
@Primary
@Override //继承上面这个类,并且加上这个之后才能把它设置为默认的。
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
String className = target.getClass().getSimpleName();
String methodName = method.getName();
StringBuilder paramBuilder = new StringBuilder();
if (params.length > 0) {
Object param = params[0];
// 参数为map自定义key=类名+方法名+map的key-value值
if (param instanceof Map) {
Map<String, Object> map = (Map<String, Object>) param;
if (!map.isEmpty()) {
for (String key : map.keySet()) {
paramBuilder.append(key).append("-").append(map.get(key)).append(CacheManagerConfiguration.SEPARATOR);
}
}
} else {
for (Object key : params) {
if (key != null) {
paramBuilder.append(JSONObject.toJSONString(key)).append(CacheManagerConfiguration.SEPARATOR);
}
}
}
}
String paramString = paramBuilder.toString().replaceAll(":", ":");
if (paramString.endsWith(CacheManagerConfiguration.SEPARATOR)) {
paramString = paramString.substring(0, paramString.length() - 1);
}
SimpleKey key = new SimpleKey(className, methodName, paramString);
return key;
};
}
}

详情请看附件

注:注意不要将ObjectMapper加入到Spring容器中,因为Spring容器中存在一个ObjectMapper,以用于@RequestBodyResponseBodyRestTemplate等地的序列化和反序列化。

为什么不采用Spring容器的ObjectMapper对象,而要自己设置是因为Redis配置了objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);属性,在序列化时记录类/属性的类型,以便在反序列化时得到POJO对象。

三、使用

1、基于声明式注释的缓存

SpringCache是Service层的声明式缓存。即无需与业务代码耦合,通过注解完成缓存。

注解通用属性:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key
keyGenerator key的生成器。key/keyGenerator二选一使用
cacheManager 指定缓存管理器
cacheResolver 指定获取解析器
condition 条件符合则缓存
unless 条件符合则不缓存
1.1、@Cacheable注解

根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。 查看源码,特殊属性值如下:

属性/方法名 解释
sync 是否使用异步模式,默认为false

附:SpringCache的元数据,可用在Key上

名称 位置 描述 例子
methodMame root 被调用方法的名称 #root.methodName
method root 被调用的方法 #root.method.name
target root 被调用的目标对象 #root.target
targetClass root 被调用目标的类 #root.targetClass
args root 用于被调用目标的参数值(数组) #root.args[0]
caches root 执行当前方法缓存的集合 #root.caches[0].name
参数名称 调用的方法 方法的任何参数名称 #iban或#a0
result 调用的方法 仅用在unless,方法调用的结果(缓存值) #result
1.2、@CachePut注解

使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上

1.3、@CacheEvict注解

使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上 查看源码,特殊属性值如下:

属性/方法名 解释
allEntries 该参数指示是否需要在整个缓存范围内逐出而不仅仅是基于Key的逐条逐出,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存
beforeInvocation 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存
1.4、@Caching注解

该注解可以实现同一个方法上同时使用多种注解。

缓存管理器CacheManager使用的更多相关文章

  1. 自定义缓存管理器 或者 Spring -- cache

    Spring Cache 缓存是实际工作中非常常用的一种提高性能的方法, 我们会在许多场景下来使用缓存. 本文通过一个简单的例子进行展开,通过对比我们原来的自定义缓存和 spring 的基于注释的 c ...

  2. SpringMVC + Mybatis + Shiro + ehcache时缓存管理器报错。

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shiroFilter' ...

  3. sitecore 缓存管理器

    namespace XXX.Shared.Infrastructure.Caching { using System; using System.Collections.Generic; using ...

  4. [转载]挂接缓存管理器CcMapData()实现文件XX

    原作者Azy,发表于DebugMan论坛. ======================================================= 这个方法的最大好处在于简单~~不用分别处理~ ...

  5. Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager

    8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...

  6. Spring自定义缓存管理及配置Ehcache缓存

    spring自带缓存.自建缓存管理器等都可解决项目部分性能问题.结合Ehcache后性能更优,使用也比较简单. 在进行Ehcache学习之前,最好对Spring自带的缓存管理有一个总体的认识. 这篇文 ...

  7. Apache-Shiro+Zookeeper系统集群安全解决方案之缓存管理

    上篇[Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理],解决了Shiro在系统集群开发时安全的会话共享问题,系统在使用过程中会有大量的权限检查和用户身份检验动作,为了不频繁 ...

  8. shiro缓存管理

    一. 概述 Shiro作为一个开源的权限框架,其组件化的设计思想使得开发者可以根据具体业务场景灵活地实现权限管理方案,权限粒度的控制非常方便.首先,我们来看看Shiro框架的架构图:从上图我们可以很清 ...

  9. Redis缓存之自定义CacheManager

    测试缓存:原理:CacheManager===Cache 缓存组件来实际给缓存中存储数据1,引入redis的starter,容器中保存的是RedisCacheManager2,RedisCacheMa ...

  10. Springboot中的缓存Cache和CacheManager原理介绍

    背景理解 什么是缓存,为什么要用缓存 程序运行中,在内存保持一定时间不变的数据就是缓存.简单到写一个Map,里面放着一些key,value数据,就已经是个缓存了 所以缓存并不是什么高大上的技术,只是个 ...

随机推荐

  1. Logstash:使用ELK堆栈进行API分析

  2. 它让你1小时精通RabbitMQ消息队列(新增死信处理)

    支持.NET/.NET Framework/.NET Core RabbitMQ作为一款主流的消息队列工具早已广受欢迎.相比于其它的MQ工具,RabbitMQ支持的语言更多.功能更完善. 本文提供一种 ...

  3. AlexNet-文献阅读笔记

    论文介绍 ImageNet Classification with Deep Convolutional Neural Networks- Alex Krizhevsky, Ilya Sutskeve ...

  4. 不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理

    我们平时开发中好像很少使用到BlockingQueue(阻塞队列),比如我们想要存储一组数据的时候会使用ArrayList,想要存储键值对数据会使用HashMap,在什么场景下需要用到Blocking ...

  5. 15. MongoDB系列之选择片键

    1. 片键类型 1.1 升序片键 升序片键通常类似于date或ObjectId--随着时间稳步增长的字段. 这种模式通常会使MongoDB更难保持块的平衡,因为所有的块都是由一个分片创建的. 1.2 ...

  6. JDBC数据库编程(java实训报告)

    文章目录 一.实验要求: 二.实验环境: 三.实验内容: 1.建立数据库连接 2.查询数据 2.1 测试结果 3.添加数据 3.1.测试结果 4.删除数据 4.1.测试结果 5.修改数据 5.1 测试 ...

  7. 齐博x1齐博首创钩子的使用方法

    齐博X1有两套钩子体系,第一套是基于TP思路设计的.跟外面的大同小异.现在重点讲一下第二套我们首创的使用方法. 首先说一下如何埋钩子,这个跟TP思路的钩子类似,就是在页面的任何地方加入如下代码即可如下 ...

  8. CJK备注

    pip清华镜像库 :pip install XXX -i https://pypi.tuna.tsinghua.edu.cn/simple pip阿里巴巴镜像库:pip install XXX -i ...

  9. 13-ORM-更新&删除

    一.更改单个数据 修改单个实体的某些字段 1.查: - 通过get()得到要修改的实体对象 2.改: - 通过对象属性的=的方式修改数据 3.保存 - 通过对象.save()保存数据     二.批量 ...

  10. 上传数据、下载模板文件解决方案(前端:antd;后端:.Net Core WebAPI)

    一.Excel 模板下载 通过静态文件下载. 将模板文件放在根目录的 public 文件夹下备用. 下载事件方法如下:(通过临时生成一个 a 标签,触发后再移除) downLoadExcelModel ...