SpringBoot集成redis

本节目标

  • 了解SpringBoot默认支持的两种操作redis的工具包
  • 采用lettuce操作redis
  • 了解redission
  • 采用redission操作redis

本节项目:

  • springboot + lettuce/jedis
  • springboot + redisson

传统方式操作redis

1.集成lettuce操作redis

1.1 SpringBoot三板斧之添加依赖
 

xml

代码解读

复制代码

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>

由于我们把spring-boot-dependencies的pom引入了,所以这个的版本也就定下来。

可以通过Maven Helper的插件来看看这个spring-boot-starter-data-redis的依赖关系,如下图:

从依赖中可以看到默认依赖的是lettuce的包,这也就是为什么我们经常说spring-boot-starter-data-redis默认使用的是lettuce。

1.2 SpringBoot三板斧之添加配置
  • 单机配置

     

    yaml

    代码解读

    复制代码

    spring: redis:   host: 192.168.0.1   port: 6379   password: t+UUVnXZ6cysf3lr/N02d>ly+mwm/1P   database: 0   connect-timeout: 10s
  • 集群配置

     

    yaml

    代码解读

    复制代码

    spring: redis:   cluster:     nodes: - 192.168.0.1:6379       - 192.168.0.2:6379       - 192.168.0.3:6379   password: t+UUVnXZ6cysf3lr/N02d>ly+mwm/1P   timeout: 10s
1.3 测试reids
 

typescript

代码解读

复制代码

@SpringBootTest public class RedisTest {    @Resource    private RedisTemplate<String, Object> redisTemplate; ​    @Test    public void testRedisTemplate() {        redisTemplate.opsForValue().set("laoma", 55);        Object laoma = redisTemplate.opsForValue().get("laoma");        Console.log("laoma:{}", laoma);   } }

通过redis的客户端工具也能看到设置值成功了:

image-20230609143803731

但是这里的值,观察的不是很明显,因为在redis操作的时候,采用的序列化是采用默认的jdk序列化对象的方式。也就是类似字节码的形式存储。

1.4 序列化

上面提到了默认采用字节码形式存储,不太便于查看。而我们经常是采用json结构把一个对象存储到redis中,首先看看RedisTemplate这个类中默认的序列化方式,源码如下:

image-20230609134853733

Redis本身提供了一下一种序列化的方式:

  • GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化
  • Jackson2JsonRedisSerializer: 序列化object对象为json字符串
  • JdkSerializationRedisSerializer: 序列化java对象
  • StringRedisSerializer: 简单的字符串序列化

如果我们存储的是String类型,默认使用的是StringRedisSerializer 这种序列化方式。如果我们存储的是对象,默认使用的是 JdkSerializationRedisSerializer,也就是Jdk的序列化方式(通过ObjectOutputStream和ObjectInputStream实现,缺点是我们无法直观看到存储的对象内容)。

正是由于不直观,所以我们一般把对象序列化方式调整为 Jackson2JsonRedisSerializer。配置类如下:

 

typescript

代码解读

复制代码

/** * redis配置序列化 * * @author 老马 * @date 2023-06-09 14:58 */ @Configuration public class RedisConfig { ​    @Resource    private RedisConnectionFactory factory; ​    @Bean    public RedisTemplate<String, Object> redisTemplate() {        RedisSerializer<Object> serializer = redisSerializer();        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();        redisTemplate.setConnectionFactory(factory);        redisTemplate.setKeySerializer(new StringRedisSerializer());        redisTemplate.setValueSerializer(serializer);        redisTemplate.setHashKeySerializer(new StringRedisSerializer());        redisTemplate.setHashValueSerializer(serializer);        redisTemplate.afterPropertiesSet();        return redisTemplate;   } ​    @Bean    public RedisSerializer<Object> redisSerializer() {        // 创建JSON序列化器        Jackson2JsonRedisSerializer<Object> serializer =                new Jackson2JsonRedisSerializer<>(Object.class);        ObjectMapper objectMapper = new ObjectMapper();        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        // 必须设置,否则无法将json转化为对象,会转化为Map类型        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,                ObjectMapper.DefaultTyping.NON_FINAL);        serializer.setObjectMapper(objectMapper);        return serializer;   } }

再次通过测试方法设置redis的值,采用redis客户端工具观察:

1.5 加入连接池

有的时候我们想要给我们的redis客户端配置上连接池。就像我们连接mysql的时候,也会配置连接池一样,目的就是增加对于数据连接的管理,提升访问的效率,也保证了对资源的合理利用。

  • 修改配置文件

     

    yaml

    代码解读

    复制代码

    spring: redis:   host: 192.168.0.1   port: 6379   password: t+UUVnXZ6cysf3lr/N02d>ly+mwm/1P   database: 0   connect-timeout: 10s   lettuce:     pool:        #连接池中的最小空闲连接       min-idle: 8        #连接池中的最大空闲连接       max-idle: 16        #连接池最大连接数(负值表示无限制)       max-active: 100
  • 加入连接池的依赖

     

    xml

    代码解读

    复制代码

    <!-- 连接池 --> <dependency>    <groupId>org.apache.commons</groupId>    <artifactId>commons-pool2</artifactId> </dependency>

    导入连接池包前后RedisTemplate对象的对比:

2.集成jedis操作redis

2.1 依赖修改
 

xml

代码解读

复制代码

<!-- reids --> <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId>    <exclusions>        <exclusion>            <artifactId>lettuce-core</artifactId>            <groupId>io.lettuce</groupId>        </exclusion>    </exclusions> </dependency> ​ <!-- jedis --> <dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId> </dependency>

这里把SpringBoot默认的lettuce排除掉,下面引入jedis。

2.2 配置修改
 

yaml

代码解读

复制代码

spring: redis:   host: 192.168.0.1   port: 6379   password: t+UUVnXZ6cysf3lr/N02d>ly+mwm/1P   database: 0   connect-timeout: 10s    # 这里仅仅把lettuce替换为jedis即可    # lettuce:   jedis:     pool:        #连接池中的最小空闲连接       min-idle: 8        #连接池中的最大空闲连接       max-idle: 16        #连接池最大连接数(负值表示无限制)       max-active: 100

2.3 进行测试

3.lettuce和jedis丝滑切换的原理

SpringBoot框架能通过各种starter进行无缝集成一个主要原因是其自动化配置功能。所谓自动化配置就是springBoot本身已经预先设置好了一些常用框架的整合类。然后通过类似于ConditionOn这样的条件判断注解,去辨别你的项目中是否有相关的类(或配置)了,进而进行相关配置的初始化。springBoot预设的自动化配置类都位于spring-boot-autoconfigure这个包中,只要我们搭建了springBoot的项目,这个包就会被引入进来。

这里可以看到这个RedisAutoConfiguration的配置类,打开看下:

再次打开引入的配置类看下:

  • lettuce

  • Jedis

至此就明白为什么我们把lettuce去掉引入jedis的依赖后,马上就采用jedis进行redis操作了。

4.redis工具类

 

typescript

代码解读

复制代码

package com.mayuanfei.springboot08.util; ​ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; ​ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; ​ /** * Redis工具类 * @author 老马 * @date 2023-06-09 17:17 */ @Component public class RedisUtil { ​    @Autowired    private RedisTemplate redisTemplate; ​    /**     * 给一个指定的 key 值附加过期时间     *     * @param key     * @param time     * @return     */    public boolean expire(String key, long time) {        return redisTemplate.expire(key, time, TimeUnit.SECONDS);   } ​    /**     * 根据key 获取过期时间     *     * @param key     * @return     */    public long getTime(String key) {        return redisTemplate.getExpire(key, TimeUnit.SECONDS);   } ​    /**     * 根据key 获取过期时间     *     * @param key     * @return     */    public boolean hasKey(String key) {        return redisTemplate.hasKey(key);   } ​    /**     * 移除指定key 的过期时间     *     * @param key     * @return     */    public boolean persist(String key) {        return redisTemplate.boundValueOps(key).persist();   } ​    //- - - - - - - - - - - - - - - - - - - - - String类型 - - - - - - - - - - - - - - - - - ​    /**     * 根据key获取值     *     * @param key 键     * @return 值     */    public Object get(String key) {        return key == null ? null : redisTemplate.opsForValue().get(key);   } ​    /**     * 将值放入缓存     *     * @param key   键     * @param value 值     * @return true成功 false 失败     */    public void set(String key, String value) {        redisTemplate.opsForValue().set(key, value);   } ​    /**     * 将值放入缓存并设置时间     *     * @param key   键     * @param value 值     * @param time 时间(秒) -1为无期限     * @return true成功 false 失败     */    public void set(String key, String value, long time) {        if (time > 0) {            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);       } else {            redisTemplate.opsForValue().set(key, value);       }   } ​    /**     * 批量添加 key (重复的键会覆盖)     *     * @param keyAndValue     */    public void batchSet(Map<String, String> keyAndValue) {        redisTemplate.opsForValue().multiSet(keyAndValue);   } ​    /**     * 批量添加 key-value 只有在键不存在时,才添加     * map 中只要有一个key存在,则全部不添加     *     * @param keyAndValue     */    public void batchSetIfAbsent(Map<String, String> keyAndValue) {        redisTemplate.opsForValue().multiSetIfAbsent(keyAndValue);   } ​    /**     * 对一个 key-value 的值进行加减操作,     * 如果该 key 不存在 将创建一个key 并赋值该 number     * 如果 key 存在,但 value 不是长整型 ,将报错     *     * @param key     * @param number     */    public Long increment(String key, long number) {        return redisTemplate.opsForValue().increment(key, number);   } ​    /**     * 对一个 key-value 的值进行加减操作,     * 如果该 key 不存在 将创建一个key 并赋值该 number     * 如果 key 存在,但 value 不是 纯数字 ,将报错     *     * @param key     * @param number     */    public Double increment(String key, double number) {        return redisTemplate.opsForValue().increment(key, number);   } ​    //- - - - - - - - - - - - - - - - - - - - - set类型 - - - - - - - - - - - - - - - - - - ​    /**     * 将数据放入set缓存     *     * @param key 键     * @return     */    public void sSet(String key, String value) {        redisTemplate.opsForSet().add(key, value);   } ​    /**     * 获取变量中的值     *     * @param key 键     * @return     */    public Set<Object> members(String key) {        return redisTemplate.opsForSet().members(key);   } ​    /**     * 随机获取变量中指定个数的元素     *     * @param key   键     * @param count 值     * @return     */    public void randomMembers(String key, long count) {        redisTemplate.opsForSet().randomMembers(key, count);   } ​    /**     * 随机获取变量中的元素     *     * @param key 键     * @return     */    public Object randomMember(String key) {        return redisTemplate.opsForSet().randomMember(key);   } ​    /**     * 弹出变量中的元素     *     * @param key 键     * @return     */    public Object pop(String key) {        return redisTemplate.opsForSet().pop("setValue");   } ​    /**     * 获取变量中值的长度     *     * @param key 键     * @return     */    public long size(String key) {        return redisTemplate.opsForSet().size(key);   } ​    /**     * 根据value从一个set中查询,是否存在     *     * @param key   键     * @param value 值     * @return true 存在 false不存在     */    public boolean sHasKey(String key, Object value) {        return redisTemplate.opsForSet().isMember(key, value);   } ​    /**     * 检查给定的元素是否在变量中。     *     * @param key 键     * @param obj 元素对象     * @return     */    public boolean isMember(String key, Object obj) {        return redisTemplate.opsForSet().isMember(key, obj);   } ​    /**     * 转移变量的元素值到目的变量。     *     * @param key     键     * @param value   元素对象     * @param destKey 元素对象     * @return     */    public boolean move(String key, String value, String destKey) {        return redisTemplate.opsForSet().move(key, value, destKey);   } ​    /**     * 批量移除set缓存中元素     *     * @param key   键     * @param values 值     * @return     */    public void remove(String key, Object... values) {        redisTemplate.opsForSet().remove(key, values);   } ​    /**     * 通过给定的key求2个set变量的差值     *     * @param key     键     * @param destKey 键     * @return     */    public Set<Set> difference(String key, String destKey) {        return redisTemplate.opsForSet().difference(key, destKey);   } ​ ​    //- - - - - - - - - - - - - - - - - - - - - hash类型 - - - - - - - - - - - - - - - - - - ​    /**     * 加入缓存     *     * @param key 键     * @param map 键     * @return     */    public void add(String key, Map<String, String> map) {        redisTemplate.opsForHash().putAll(key, map);   } ​    /**     * 获取 key 下的 所有 hashkey 和 value     *     * @param key 键     * @return     */    public Map<Object, Object> getHashEntries(String key) {        return redisTemplate.opsForHash().entries(key);   } ​    /**     * 验证指定 key 下 有没有指定的 hashkey     *     * @param key     * @param hashKey     * @return     */    public boolean hashKey(String key, String hashKey) {        return redisTemplate.opsForHash().hasKey(key, hashKey);   } ​    /**     * 获取指定key的值string     *     * @param key 键     * @param key2 键     * @return     */    public String getMapString(String key, String key2) {        return redisTemplate.opsForHash().get("map1", "key1").toString();   } ​    /**     * 获取指定的值Int     *     * @param key 键     * @param key2 键     * @return     */    public Integer getMapInt(String key, String key2) {        return (Integer) redisTemplate.opsForHash().get("map1", "key1");   } ​    /**     * 弹出元素并删除     *     * @param key 键     * @return     */    public String popValue(String key) {        return redisTemplate.opsForSet().pop(key).toString();   } ​    /**     * 删除指定 hash 的 HashKey     *     * @param key     * @param hashKeys     * @return 删除成功的 数量     */    public Long delete(String key, String... hashKeys) {        return redisTemplate.opsForHash().delete(key, hashKeys);   } ​    /**     * 给指定 hash 的 hashkey 做增减操作     *     * @param key     * @param hashKey     * @param number     * @return     */    public Long increment(String key, String hashKey, long number) {        return redisTemplate.opsForHash().increment(key, hashKey, number);   } ​    /**     * 给指定 hash 的 hashkey 做增减操作     *     * @param key     * @param hashKey     * @param number     * @return     */    public Double increment(String key, String hashKey, Double number) {        return redisTemplate.opsForHash().increment(key, hashKey, number);   } ​    /**     * 获取 key 下的 所有 hashkey 字段     *     * @param key     * @return     */    public Set<Object> hashKeys(String key) {        return redisTemplate.opsForHash().keys(key);   } ​    /**     * 获取指定 hash 下面的 键值对 数量     *     * @param key     * @return     */    public Long hashSize(String key) {        return redisTemplate.opsForHash().size(key);   } ​    //- - - - - - - - - - - - - - - - - - - - - list类型 - - - - - - - - - - - - - - - - - - ​    /**     * 在变量左边添加元素值     *     * @param key     * @param value     * @return     */    public void leftPush(String key, Object value) {        redisTemplate.opsForList().leftPush(key, value);   } ​    /**     * 获取集合指定位置的值。     *     * @param key     * @param index     * @return     */    public Object index(String key, long index) {        return redisTemplate.opsForList().index("list", 1);   } ​    /**     * 获取指定区间的值。     *     * @param key     * @param start     * @param end     * @return     */    public List<Object> range(String key, long start, long end) {        return redisTemplate.opsForList().range(key, start, end);   } ​    /**     * 把最后一个参数值放到指定集合的第一个出现中间参数的前面,     * 如果中间参数值存在的话。     *     * @param key     * @param pivot     * @param value     * @return     */    public void leftPush(String key, String pivot, String value) {        redisTemplate.opsForList().leftPush(key, pivot, value);   } ​    /**     * 向左边批量添加参数元素。     *     * @param key     * @param values     * @return     */    public void leftPushAll(String key, String... values) {        redisTemplate.opsForList().leftPushAll(key, values);   } ​    /**     * 向集合最右边添加元素。     *     * @param key     * @param value     * @return     */    public void leftPushAll(String key, String value) {        redisTemplate.opsForList().rightPush(key, value);   } ​    /**     * 向左边批量添加参数元素。     *     * @param key     * @param values     * @return     */    public void rightPushAll(String key, String... values) {        redisTemplate.opsForList().rightPushAll(key, values);   } ​    /**     * 向已存在的集合中添加元素。     *     * @param key     * @param value     * @return     */    public void rightPushIfPresent(String key, Object value) {        redisTemplate.opsForList().rightPushIfPresent(key, value);   } ​    /**     * 向已存在的集合中添加元素。     *     * @param key     * @return     */    public long listLength(String key) {        return redisTemplate.opsForList().size(key);   } ​    /**     * 移除集合中的左边第一个元素。     *     * @param key     * @return     */    public void leftPop(String key) {        redisTemplate.opsForList().leftPop(key);   } ​    /**     * 移除集合中左边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。     *     * @param key     * @return     */    public void leftPop(String key, long timeout, TimeUnit unit) {        redisTemplate.opsForList().leftPop(key, timeout, unit);   } ​    /**     * 移除集合中右边的元素。     *     * @param key     * @return     */    public void rightPop(String key) {        redisTemplate.opsForList().rightPop(key);   } ​    /**     * 移除集合中右边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。     *     * @param key     * @return     */    public void rightPop(String key, long timeout, TimeUnit unit) {        redisTemplate.opsForList().rightPop(key, timeout, unit);   } }

5.代码地址

gitee.com/mayuanfei/S…下的springboot08

redission方式操作redis

1.简介

redisson是Redis官方推荐的一个客户端工具。它提供了使用Redis的最简单和最便捷的方法。除此之外,redisson还提供了可靠的分布式锁、布隆过滤器等功能。

文档地址: redisson中文文档

2.SpringBoot三板斧之添加依赖

官方比较推荐的starter包配置方式,这种方式会深度和Spring Data Redis模块结合。所以官方要求redisson-spring-data要和springboot的版本配合使用。否则有可能会引起莫名的问题。对应关系如下:

redisson-spring-data module nameSpring Boot version
redisson-spring-data-161.3.y
redisson-spring-data-171.4.y
redisson-spring-data-181.5.y
redisson-spring-data-2x2.x.y
redisson-spring-data-3x3.x.y

这也就是说,redisson的starter可以比较新,但是redisson-spring-data要和springboot的版本对应。示例中的springboot的版本为2.7.10版本,pom引入:

 

xml

代码解读

复制代码

<!-- redisson--> <dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson-spring-boot-starter</artifactId>    <version>3.21.3</version>    <exclusions>        <exclusion>            <artifactId>redisson-spring-data-30</artifactId>            <groupId>org.redisson</groupId>        </exclusion>    </exclusions> </dependency> <dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson-spring-data-27</artifactId>    <version>3.21.3</version> </dependency>

3.SpringBoot三板斧之添加配置

 

yaml

代码解读

复制代码

spring: redis:    # 地址   host: 192.168.0.1    # 端口,默认为6379   port: 6379    # 数据库索引   database: 0    # 密码(如没有密码请注释掉)   password: t+UUVnXZ6cys23dsd2d>ly+mwm/1P    # 连接超时时间   timeout: 10s    # 是否开启ssl   ssl: false # redisson配置 redisson:  # redis key前缀 keyPrefix: springboot09  # 线程池数量 threads: 4  # Netty线程池数量 nettyThreads: 8  # 单节点配置 singleServerConfig:    # 客户端名称   clientName: laoma_macos    # 最小空闲连接数   connectionMinimumIdleSize: 8    # 连接池大小   connectionPoolSize: 32    # 连接空闲超时,单位:毫秒   idleConnectionTimeout: 10000    # 命令等待超时,单位:毫秒   timeout: 3000    # 发布和订阅连接池大小   subscriptionConnectionPoolSize: 50    

4.redisson配置类

redisson不但能操作redis,还依照Spring Cache标准提供了基于Redis的Spring缓存实现,每个缓存(Cache)实例都提供了了两个重要的可配置参数:过期时间(ttl)和最长空闲时间(maxIdleTime),如果这两个参数都未指定或值为0,那么实例管理的数据将永久保存。相对而已还比较复杂,配置类代码如下:

 

less

代码解读

复制代码

@Slf4j @Configuration @EnableCaching @EnableConfigurationProperties(RedissonProperties.class) public class RedisConfig { ​    @Resource    private RedissonProperties redissonProperties; ​    @Resource    private ObjectMapper objectMapper; ​    @Bean    public RedissonAutoConfigurationCustomizer redissonCustomizer() {        return config -> {            config.setThreads(redissonProperties.getThreads())               .setNettyThreads(redissonProperties.getNettyThreads())               .setCodec(new JsonJacksonCodec(objectMapper));            RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();            if (ObjectUtil.isNotNull(singleServerConfig)) {                // 使用单机模式                config.useSingleServer()                   .setTimeout(singleServerConfig.getTimeout())                   .setClientName(singleServerConfig.getClientName())                   .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())                   .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())                   .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())                   .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());           }            // 集群配置方式            RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();            if (ObjectUtil.isNotNull(clusterServersConfig)) {                config.useClusterServers()                   .setTimeout(clusterServersConfig.getTimeout())                   .setClientName(clusterServersConfig.getClientName())                   .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())                   .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())                   .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())                   .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())                   .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())                   .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())                   .setReadMode(clusterServersConfig.getReadMode())                   .setSubscriptionMode(clusterServersConfig.getSubscriptionMode());           }            log.info("初始化 redis 配置");       };   }     /**     * reission实现的缓存管理器 整合spring-cache     */    @Bean    public CacheManager cacheManager() {        return new RedissonSpringCacheManager(SpringUtil.getBean(RedissonClient.class));   } }

这里配置的cacheManager采用的默认的redisson类。这种方式少了过期时间、最大空闲时间和组最大长度的配置,如果想更完善的使用spring cache,可以参考:blog.youkuaiyun.com/weixin_4046…这里进行配置。

5.测试类

Spring-cache注释的使用参考:blog.youkuaiyun.com/m0_67698950…

 

kotlin

代码解读

复制代码

/** * 表示这个方法有了缓存的功能,方法的返回值会被缓存下来 * 下一次调用该方法前,会去检查是否缓存中已经有值 * 如果有就直接返回,不调用方法 * 如果没有,就调用方法,然后把结果缓存起来 * 这个注解「一般用在查询方法上」 */ @Cacheable(cacheNames = "test:demo", key = "#userid", condition = "#userid != null") @GetMapping("/test3") public SysUser testRedis3(Long userid) {    SysUser user = new SysUser(userid, "测试用户" + userid, "昵称" + userid);    return user; } ​ /** * 会把方法的返回值put到缓存里面缓存起来,供其它地方使用 * 它「通常用在新增或者实时更新方法上」 */ @CachePut(cacheNames = "test:demo", key = "#userid", condition = "#userid != null") @GetMapping("/test4") public SysUser testRedis4(Long userid) {    SysUser user = new SysUser(userid, "测试用户" + userid, "昵称" + userid);    return user; } ​ /** * 使用了CacheEvict注解的方法,会清空指定缓存 * 「一般用在删除的方法上」 */ @CacheEvict(cacheNames = "test:demo", key = "#userid", condition = "#userid != null") @GetMapping("/test5") public String testRedis5(Long userid) {    return "删除成功"; }

6.代码地址

gitee.com/mayuanfei/S…下的springboot09

redisson极简模式集成及其分布式锁使用

1.极简模式集成redisson

1.1加入依赖
 

xml

代码解读

复制代码

<!-- redisson--> <dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson-spring-boot-starter</artifactId>    <version>3.21.3</version> </dependency>

1.2添加配置
 

yaml

代码解读

复制代码

# redis基础配置 spring: redis:    # 地址   host: 192.168.0.1    # 端口,默认为6379   port: 6379    # 数据库索引   database: 0    # 密码(如没有密码请注释掉)   password: t+UUVnXZ6cys23dsd2d>ly+mwm/1P    # 连接超时时间   timeout: 10s    # 是否开启ssl   ssl: false

如果想对redisson有更多的配置,可以加入redssion的配置文件

 

yaml

代码解读

复制代码

spring: redis:   redisson:     file: classpath:redisson.yaml ​

配置文件可以参考: redisson中文文档

1.3测试通过redisson操作redis
 

typescript

代码解读

复制代码

@Resource private RedissonClient redissonClient; ​ // 通过redisson操作redis @GetMapping("/test1") public String testRedis1() {    JsonJacksonCodec jacksonCodec = new JsonJacksonCodec();    Map<String, Integer> map  = new HashMap<>();    map.put("laoma", 44);    map.put("cuidk", 43);    RMap<Object, Object> lala = this.redissonClient.getMap("lala", jacksonCodec);    lala.putAll(map);    RBucket<Object> bucket = this.redissonClient.getBucket("aa:bb:cc", jacksonCodec);    bucket.set(124);    return JSONUtil.toJsonStr(lala); }

2.redisson实现分布式锁

分布式锁的应用场景:我们在用nginx把同一个程序进行集群部署时,由于每一个springboot程序都是单独的JVM,所以传统的synchronized和ReentrantLock已经都不好使了。这时,就考虑用redisson的分布式锁。

2.1锁原理

image-20230614110451194

2.2示例代码
 

csharp

代码解读

复制代码

@Resource private RedissonClient redissonClient; ​ public void updateUser(String userId) {  String lockKey = "user" + userId;  RLock lock = redissonClient.getLock(lockKey);  //获取锁资源  try {    lock.lock(10, TimeUnit.SECONDS);   //加锁,可以指定锁定时间 ​    //这里写需要处理业务的业务代码 } finally {    lock.unlock();   //释放锁 } } ​

2.3代码地址

gitee.com/mayuanfei/S…下的springboot10

记忆印记

  • springboot默认继承lettuce来操作redis的
  • 可以利用redisson来操作redis,而且redisson封装了很多好用的方法。以后操作redis优先使用redisson
  • redisson可以把java现有的类型,缓存到redis中,也就是可以在集群系统中,像访问同一个数据库一样
  • 在集群或者分布式系统中,我们经常用redisson来实现分布式锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值