缓存管理器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. ProxySQL(12):禁止多路路由

    文章转载自:https://www.cnblogs.com/f-ck-need-u/p/9372447.html multiplexing multiplexing,作用是将语句分多路路由.开启了mu ...

  2. parted创建磁盘分区并创建LVM(Linux合并多块大于2T的磁盘并合并到一个分区)

    文章转载自:https://blog.csdn.net/likemebee/article/details/85630808

  3. 在Ubuntu 主机上使用 Cockpit 管理容器

    如果你管理着一台 Linux 服务器,那么你可能正在寻找一个可靠的管理工具.为了这个你可能已经看了 Webmin 和 cPanel 这类软件.但是,如果你正在寻找一种简单的方法来管理还包括了 Dock ...

  4. 【前端必会】单页应用-你的新朋友wepack

    背景 我们开发的功能可能是简单的,但是实现功能的代码行数却可能成千上万 出于易于维护.安全.服用,我们会根据我们的经验设计我们的代码,拆解成多个独立的功能模块(代码片段.更多的文件) JS的模块规范有 ...

  5. echarts pie饼状图绑定点击事件

    var valueData = [ {value: 33,name: '诊所'}, {value: 29,name: '汽车服务相关'}, {value: 27, name: '洗衣店'}, {val ...

  6. P4047 [JSOI2010]部落划分 方法记录

    原题链接 [JSOI2010]部落划分 题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常 ...

  7. Nginx代理和动静分离

    Nginx代理 微服务项目可能需要 Nginx来实现反向代理,用户请求 Nginx,随后 Nginx将请求转发至 Gateway网关,再由网关转至具体的微服务 一.动态代理 1.1 网关配置 针对使用 ...

  8. Bug改不完,迭代总延期,咋办?

    摘要:本文从流程上需要改进的地方进行讨论,分四个方面来分析产生这个问题的原因. 本文分享自华为云社区<Bug改不完,迭代总延期,咋办?>,作者: 华为云PaaS服务小智. 前言 随着互联网 ...

  9. 删除数组里含有a的元素,并且将null值放在后面

    想去掉里面含有a的元素,并将null放在后面.放在后面就是往后移,其他值往左移 1 public static void main(String[] args) { 2 //自定义的一个数组 3 St ...

  10. python基础之if条件控制语句

    前言 本文主要介绍控制流程中的if条件语句,包括if...:if...else...:if...elif...elif...else...:if...if...if...else...:if嵌套等.内 ...