缓存管理器CacheManager使用
缓存管理器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,以用于@RequestBody、ResponseBody、RestTemplate等地的序列化和反序列化。
为什么不采用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使用的更多相关文章
- 自定义缓存管理器 或者 Spring -- cache
Spring Cache 缓存是实际工作中非常常用的一种提高性能的方法, 我们会在许多场景下来使用缓存. 本文通过一个简单的例子进行展开,通过对比我们原来的自定义缓存和 spring 的基于注释的 c ...
- SpringMVC + Mybatis + Shiro + ehcache时缓存管理器报错。
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shiroFilter' ...
- sitecore 缓存管理器
namespace XXX.Shared.Infrastructure.Caching { using System; using System.Collections.Generic; using ...
- [转载]挂接缓存管理器CcMapData()实现文件XX
原作者Azy,发表于DebugMan论坛. ======================================================= 这个方法的最大好处在于简单~~不用分别处理~ ...
- Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager
8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...
- Spring自定义缓存管理及配置Ehcache缓存
spring自带缓存.自建缓存管理器等都可解决项目部分性能问题.结合Ehcache后性能更优,使用也比较简单. 在进行Ehcache学习之前,最好对Spring自带的缓存管理有一个总体的认识. 这篇文 ...
- Apache-Shiro+Zookeeper系统集群安全解决方案之缓存管理
上篇[Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理],解决了Shiro在系统集群开发时安全的会话共享问题,系统在使用过程中会有大量的权限检查和用户身份检验动作,为了不频繁 ...
- shiro缓存管理
一. 概述 Shiro作为一个开源的权限框架,其组件化的设计思想使得开发者可以根据具体业务场景灵活地实现权限管理方案,权限粒度的控制非常方便.首先,我们来看看Shiro框架的架构图:从上图我们可以很清 ...
- Redis缓存之自定义CacheManager
测试缓存:原理:CacheManager===Cache 缓存组件来实际给缓存中存储数据1,引入redis的starter,容器中保存的是RedisCacheManager2,RedisCacheMa ...
- Springboot中的缓存Cache和CacheManager原理介绍
背景理解 什么是缓存,为什么要用缓存 程序运行中,在内存保持一定时间不变的数据就是缓存.简单到写一个Map,里面放着一些key,value数据,就已经是个缓存了 所以缓存并不是什么高大上的技术,只是个 ...
随机推荐
- 使用TLS安全的访问Minio服务
官方文档地址:http://docs.minio.org.cn/docs/master/how-to-secure-access-to-minio-server-with-tls 查看这篇文章的操作步 ...
- k8s中计算资源策略 Limit Range
文章转载自:https://www.kuboard.cn/learning/k8s-advanced/policy/lr.html 默认情况下,容器在 Kubernetes 集群上运行时,不受 计算资 ...
- 查看当前看k8s集群支持的版本命令
# kubectl api-versions admissionregistration.k8s.io/v1 admissionregistration.k8s.io/v1beta1 apiexten ...
- ELK基于ElastAlert实现日志的微信报警 ---docker环境
参考网址:https://github.com/anjia0532/elastalert-docker 1.拉取镜像: docker pull anjia0532/elastalert-docker: ...
- NLP之TextRNN(预测下一个单词)
TextRNN @ 目录 TextRNN 1.基本概念 1.1 RNN和CNN的区别 1.2 RNN的几种结构 1.3 多对多的RNN 1.4 RNN的多对多结构 1.5 RNN的多对一结构 1.6 ...
- vuex和浏览器【sessionStorage,localStorage ..】 得区别
1.Vuex的主要作用是用来共享和管理数据,那为什么不直接使用浏览器缓存呢?2.Vuex和浏览器缓存策略的根本区别是什么?3.Vuex,state仓库中的数据流是单向同步的,那为什么可以在action ...
- 二十八、Helm
使用Helm3管理复杂应用的部署 认识Helm 为什么有helm? Helm是什么? kubernetes的包管理器,"可以将Helm看作Linux系统下的apt-get/yum" ...
- Debian11管理员手册
1 用户与群组数据库 用户清单通常保存在 /etc/passwd 文件内,把哈希编码后的密码保存在 /etc/shadow 文件内.这两个文件都是纯文本档,以简单的格式保存,可以用文本编辑器读取与修改 ...
- 通过tkinter列出全部字体名称
通过tkinter列出windows系统全部字体名称 通过 tkinter.font 的 families() 函数实现 import tkinter import tkinter.font # 把p ...
- react的组件通信
react的组件通信 1.父组件传子组件 import React, {Component} from 'react'class Father extends Component{ render() ...