首页 > 其他分享 > 缓存管理器CacheManager使用

缓存管理器CacheManager使用

时间:2022-12-06 14:59:07浏览次数:55  
标签:mapper 缓存 管理器 方法 key CacheManager 序列化 root

缓存管理器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注解

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

标签:mapper,缓存,管理器,方法,key,CacheManager,序列化,root
From: https://www.cnblogs.com/checkcode/p/CacheManager.html

相关文章

  • Xcode清除缓存、清理多余证书教程(傻瓜版)​
     Xcode清除缓存、清理多余证书​1、删除Xcode中多余的证书provisioningprofile​手动删除:​Xcode6provisioningprofilepath:​~/Library/MobileDevice/Provisioni......
  • Redis 缓存击穿问题
    解决方案一:互斥锁假设一个热门产品的缓存时间到期了,那么将会有大量的请求查询不到缓存,就只能去查询数据库然后再把数据添加到缓存中。但是如果在缓存时间到期的瞬间有很多......
  • 字节二面,居然让我写一个 LFU 缓存策略算法,懵了!
    LRU全称"LeastRecentlyUsed",最近最少使用策略,判断最近被使用的时间,距离目前最远的数据优先被淘汰,作为一种根据访问时间来更改链表顺序从而实现缓存淘汰的算法,它是redis......
  • 包管理器
    包管理器​ 随着我们项目的复杂的提升,在开发中我们不可能所有的代码都要手动一行一行的编写,于是我们就需要将一些现成写好的代码引入到我们的项目中来帮助我们完成开发,就......
  • 微信小程序写入缓存再页面跳转,部分机型异常处理
    先看下发现异常的代码:uni.setStorage({ key:"tmp_registerPageInfo", data:_registerPageInfo})uni.navigateTo({ url:"/attestationPackage/pages/bankCard/mai......
  • 缓存行Cache Line
    CacheLine:顾名思义叫做缓存行缓存行越大,局部空间效率越高,读取时间越慢!缓存行越小,局部空间效率越低,读取时间越快!众所周知,计算机将数据从主存读入Cache时,是把要读取......
  • 缓存
    为什么会用到缓存?不同设备的速度不同,磁盘,内存,网络在什么地方用缓存?用户不会修改的:状态的数字与名字的对应,前端缓存地址码->国家,省市区的名字,nginx缓存,key:api+参数,va......
  • 用Java实现分布式缓存(1)——缓存淘汰
    本文代码https://github.com/weloe/Java-Distributed-Cache/tree/main/src/main/java/com/weloe/cache/outstrategyhttps://github.com/weloe/Java-Distributed-Cache/tr......
  • 高速缓存的工作原理
    Cache的基本原理我们先来看一个简单的cache,处理器每次请求一个字,并且每个块由一个单独的字组成。下图展示了该简单cache在请求数据项(该数据项初始不在cache中)前后的状态。......
  • #yyds干货盘点#前端keepalive缓存清理
    说到​​Vue​​​缓存,我们肯定首先选择官方提供的缓存方案​​keep-alive​​内置组件来实现。​​keep-alive​​组件提供给我们缓存组件的能力,可以完整的保存当前组......