SpringCache -- Redis --- 配置与缓存使用--配置过期时间--redis宕机

写在前面:
学redis,还是得搭配SpringCache来玩一玩。
前置内容

导入依赖

        <!--redis依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--redis连接池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!--SpringCache缓存 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

配置cache

在yml下

spring:
	cache:
		type: redis
		redis:
			key-prefix: Client_ #key前缀,如果配置了cacheManager则失效
     		use-key-prefix: true #是否开启key前缀
      		cache-null-values: true #是否缓存空值

在配置类上加上@EnableCaching开启缓存
配置序列化器
RedisConfig implements CachingConfigurer
redis的序列化器配置见前文,

    /**
     * 对缓存进行序列化和反序列化的配置
     * @param factory redis连接工厂
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1)) // 配置缓存时长
                // 注意0为永久用于 Duration.ZERO 声明永久缓存。
                .prefixCacheNameWith("Client:") // 前缀
                .computePrefixWith(cacheName -> "caching:" + cacheName); //前缀动态
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) // key序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); // value序列化方式
        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
    }

使用

在这里配置就差不多了,在需要进行缓存操作的地方加注解就可以了

@Cacheable

该注解为缓存获取注解:

如果缓存中没有则进行查询在添加到缓存,如果缓存有则直接走缓存

属性

  • value/cacheName
    他们互为别名
    确定目标高速缓存(或高速缓存),与特定 Bean 定义的限定符值或 Bean 名匹配
    最终缓存的key为prefix+cacheName:: key
  • key
    指定缓存的key,可以使用Spring Expression Language (SpEL) 表达式来进行动态设置。

SpEL 表达式:

  1. 可以使用 #root.method, #root.target, and #root.caches 来指定 方法, 目标对象和受影响的缓存的method引用
    @cacheable(value =“phone”,key="#root.methodName”)
    指定方法名来作为
  2. 可以使用#形参名字来使用,下面是用phone的id来
    @Cacheable(value = “Phone”,key=“#phone.id”)
    public Result<List> getAll(Phone phone)
  3. 方法参数可以通过索引访问。例如,可以通过 或 #p1 #a1访问#root.args[1]第二个参数。如果该信息可用,也可以按名称访问参数。
  • cacheManager
    指定管理器
  • condition
    缓存的条件,可以为空,使用SPEL表达式编写,返回true或者false,true表示存入缓存
  • KeyGenerator
    指定key生成对策
    如果是自定义对象类型,判断不了可以自定义KeyGenerator
@Component
public class MyGenerator implements KeyGenerator{
	@override
	public Object generate(object o, Method method, object... objects){
 		
	}
}
  • unless
    用于否决缓存放置操作的 Spring 表达式语言 (SpEL) 表达式。如果条件的计算结果为 true。
    默认值为 “”,表示缓存永远不会被否决。
  • cacheResolver
    要使用的自定义 org.springframework.cache.interceptor.CacheResolver 的 Bean 名称。

@CachePut

更新缓存
每次都会调用方法,把返回值缓存起来,每次都会更新/新增
注解和前面一样

@CacheEvict

删除缓存
调用后删除指定缓存
注解相同,多了几个

  • allEntries
    是否删除缓存中的所有条目。
  • beforeInvocation
    是否应在调用该方法之前进行逐出。
    将此属性设置为 true,会导致逐出发生,而不考虑方法结果(即,是否引发异常)。

配置过期时间

我是2种方法都配置了的

依据cacheName设置

主要在与对于每一个cacheName设置不同的RedisCacheConfiguration
多个cacheName就加多个withCacheConfiguration就可以了

    /**
     * 对缓存进行序列化和反序列化的配置
     *
     * @param factory redis连接工厂
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1)) // 配置缓存时长
                .prefixCacheNameWith("Client:") // 前缀
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) // key序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); // value序列化方式
                // 如果不需要第二种就把CustomRedisCacheManager换成RedisCacheManager
        return CustomRedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(factory)
                .cacheDefaults(config)
                .withCacheConfiguration("contact:relation", getCacheConfigurationWithTtl(redisTemplate, Duration.ofMinutes(30)))
                .transactionAware()
                .build();
    }
    RedisCacheConfiguration getCacheConfigurationWithTtl(RedisTemplate<String, Object> template, Duration time) {
        return RedisCacheConfiguration
                .defaultCacheConfig()
                .prefixCacheNameWith("Client:") // 前缀
                // 设置key为String
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getStringSerializer()))
                // 设置value 为自动转Json的Object
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getValueSerializer()))
                // 不缓存null
                .disableCachingNullValues()
                // 缓存数据保存1小时
                .entryTtl(time);
    }

在注解上截取过期时间

在cacheName上面加上#时间就可以了


/**
 * 自定义缓存管理器
 */
public class CustomRedisCacheManager extends RedisCacheManager {

    public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }

    /**
     * 针对@Cacheable设置缓存过期时间
     * @param name
     * @param cacheConfig
     * @return
     */
    @Override
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
        String[] array = StringUtils.delimitedListToStringArray(name, "#");
        // 解析TTL
        if (array.length > 2) {
            char c = array[1].charAt(array.length - 1);
            long ttl = Long.parseLong(StrUtil.sub(array[1], 0, array[1].length() - 1));
            cacheConfig = switch (c){
                case 's','S' -> cacheConfig.entryTtl(Duration.ofSeconds(ttl));
                case 'm','M' -> cacheConfig.entryTtl(Duration.ofMinutes(ttl));
                case 'h','H' -> cacheConfig.entryTtl(Duration.ofHours(ttl));
                case 'd','D' -> cacheConfig.entryTtl(Duration.ofDays(ttl));
                default -> cacheConfig.entryTtl(Duration.ofSeconds(Long.parseLong(array[1])));
            }
            cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl)); // 注意单位我此处用的是秒,而非毫秒
        }
        return super.createRedisCache(array[0], cacheConfig);
    }

}

如果redis宕机的解决方案。

redis如果连接不上,springboot执行报错,从而导致正常业务造成错误。

所以我们需要重写异常处理的方法
重写继承CachingConfigurer的errorHandler方法,直接打印就可以了。

 /**
     * 配置当redis连接不上时被缓存注解标注的方法绕过Redis
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        return new CacheErrorHandler() {
            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                log.error("redis异常:key=[{}]", key, exception);
            }

            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                log.error("redis异常:key=[{}]", key, exception);
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                log.error("redis异常:key=[{}]", key, exception);
            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                log.error("redis异常:", exception);
            }
        };
    }

问题:很久才有响应结果。

缩小redis的读取超时timeout时间。

但是我发现需要2个timeout时间才会执行正常的逻辑,所以我选择700ms

  redis:
    database: 5
    host: localhost
    port: 6379
    timeout: 700ms
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小余

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值