SpringBoot 2.x 使用Redis作为缓存 设置有效时间及自动更新策略

本文基于Springboot2.0.4 数据库使用mysql

由于在redis的客户端上采用了Letture
这里讲一下jedis和Letture的简单说明

  • Lettuce 和 Jedis 的定位都是Redis的client 都可以直接连接redis server
  • Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接
  • Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例

所以一开始在启动程序的时候就遇到这个错误

Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_91]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_91]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_91]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_91]
    ... 49 common frames omitted

在pom文件中引入下面的包

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
  1. 与Redis相关的POM依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. Application里面的配置
    这里使用的time-to-live是对所有redis缓存统一配置的时间 实际使用会有不方便的地方 可能不同的缓存需要不同的超时时间
spring:
  application:
    name: redis-demo
  cache:
    type: redis
    redis:
      time-to-live: 20000 #缓存超时时间ms
      cache-null-values: false #是否缓存空值
  redis:
    port: 6379
    host: localhost
    lettuce:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
    timeout: 10000  #redis 连接超时时间ms
    database: 0
  1. RedisCacheManager的配置
    单独为不同的缓存可以配置不同的超时时间
    disableCachingNullValues 不缓存空值
    网上很多教程的配置是
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        defaultCacheConfig.entryTtl(Duration
                .ofSeconds(30L));
        defaultCacheConfig.disableCachingNullValues();

这种配置是错误的 看完entryTtl和disableCachingNullValues的返回值均为RedisCacheConfiguration 所以上面的配置方法是无效的
下面是配置源码

@Bean
    CacheManager cacheManager(RedisConnectionFactory connectionFactory) {

        /* 默认配置, 默认超时时间为30s */
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration
                .ofSeconds(30L)).disableCachingNullValues();

        /* 配置test的超时时间为120s*/
        RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter
                (connectionFactory)).cacheDefaults(defaultCacheConfig).withInitialCacheConfigurations(singletonMap
                ("test", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(120L))
                        .disableCachingNullValues())).transactionAware().build();

        return cacheManager;
    }
  1. 注解式的Redis处理
    注解式的使用就是在方法上面加上Cacheable / CacheEvict / CachePut的注解
    注解支持使用EL表达式 这里就是支持使用相关的参数和属性来表示
    #root.targetClass 是类名
    #p0是第一个参数值
@Cacheable(value = "test", key = "#root.targetClass + '_' + #p0 + '_' + #p1")

到此使用简单的注解式的redis缓存配置就结束了
实际在项目中会遇到某些特殊的场景 某些缓存更希望用一个线程负责更新缓存 而不是单独的请求去判断 本文缓存更新采用了RedisTemplate手动写入的方式
5. RedisTemplate的配置
一开始使用的序列化方式不对 导致序列化出来的和上面系统自动缓存的不一致,导致上面读取缓存的时候总是值错误 在网上看了很多资料 后来看了源码 试了很多序列化方式 发现默认的是这个序列化类JdkSerializationRedisSerializer
在同时使用了上面注解的缓存和这种手动缓存的时候 特别需要注意的就是这个序列化方式的一致性 也可以改上面默认的序列化方式

     @Bean
    public RedisTemplate<String, Integer> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Integer> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setExposeConnection(true);
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
  1. 使用RedisTemplate进行增删
    封装一个类用于手动对缓存进行操作 还有其他操作 这个demo用不到就没有做 这里只有增加和删除两种操作
@Component
public class MyRedisCacheManager {

    @Autowired
    private RedisTemplate redisTemplate;

    /* 插入数据或者更新数据 */
    public void insert(String key, Object value, long timeout, TimeUnit timeUnit) {

        if (StringUtils.isBlank(key) || !ObjectUtils.anyNotNull(value)) {
            return;
        }
        if (timeout == 0) {
            redisTemplate.opsForValue().set(key, value);
        } else {
            redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
        }

    }

    public void delete(String key) {
        redisTemplate.opsForValue().getOperations().delete(key);
    }
}

这里是整个项目的源码 可供参考GITHUB

微信公众号不定时更新中
微信公众号:yingzi_code

猜你喜欢

转载自blog.csdn.net/yingziisme/article/details/81463391