本节目标
- 了解SpringBoot默认支持的两种操作redis的工具包
- 采用lettuce操作redis
- 了解redission
- 采用redission操作redis
本节项目:
- springboot + lettuce/jedis
- springboot + redisson
传统方式操作redis
1.集成lettuce操作redis
1.1 SpringBoot三板斧之添加依赖
<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三板斧之添加配置
-
单机配置
spring: redis: host: 192.168.0.1 port: 6379 password: t+UUVnXZ6cysf3lr/N02d>ly+mwm/1P database: 0 connect-timeout: 10s -
集群配置
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
@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的客户端工具也能看到设置值成功了:
但是这里的值,观察的不是很明显,因为在redis操作的时候,采用的序列化是采用默认的jdk序列化对象的方式。也就是类似字节码的形式存储。
1.4 序列化
上面提到了默认采用字节码形式存储,不太便于查看。而我们经常是采用json结构把一个对象存储到redis中,首先看看RedisTemplate这个类中默认的序列化方式,源码如下:
Redis本身提供了一下一种序列化的方式:
- GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化
- Jackson2JsonRedisSerializer: 序列化object对象为json字符串
- JdkSerializationRedisSerializer: 序列化java对象
- StringRedisSerializer: 简单的字符串序列化
如果我们存储的是String类型,默认使用的是StringRedisSerializer 这种序列化方式。如果我们存储的是对象,默认使用的是 JdkSerializationRedisSerializer,也就是Jdk的序列化方式(通过ObjectOutputStream和ObjectInputStream实现,缺点是我们无法直观看到存储的对象内容)。
正是由于不直观,所以我们一般把对象序列化方式调整为 Jackson2JsonRedisSerializer。配置类如下:
/**
* 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的时候,也会配置连接池一样,目的就是增加对于数据连接的管理,提升访问的效率,也保证了对资源的合理利用。
-
修改配置文件
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 -
加入连接池的依赖
<!-- 连接池 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>导入连接池包前后RedisTemplate对象的对比:
2.集成jedis操作redis
2.1 依赖修改
<!-- 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 配置修改
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工具类
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 name | Spring Boot version |
|---|---|
| redisson-spring-data-16 | 1.3.y |
| redisson-spring-data-17 | 1.4.y |
| redisson-spring-data-18 | 1.5.y |
| redisson-spring-data-2x | 2.x.y |
| redisson-spring-data-3x | 3.x.y |
这也就是说,redisson的starter可以比较新,但是redisson-spring-data要和springboot的版本对应。示例中的springboot的版本为2.7.10版本,pom引入:
<!-- 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三板斧之添加配置
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,那么实例管理的数据将永久保存。相对而已还比较复杂,配置类代码如下:
@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.csdn.net/weixin_4046…这里进行配置。
5.测试类
Spring-cache注释的使用参考:blog.csdn.net/m0_67698950…
/**
* 表示这个方法有了缓存的功能,方法的返回值会被缓存下来
* 下一次调用该方法前,会去检查是否缓存中已经有值
* 如果有就直接返回,不调用方法
* 如果没有,就调用方法,然后把结果缓存起来
* 这个注解「一般用在查询方法上」
*/
@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加入依赖
<!-- redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.21.3</version>
</dependency>
1.2添加配置
# 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的配置文件
spring:
redis:
redisson:
file: classpath:redisson.yaml
配置文件可以参考: redisson中文文档
1.3测试通过redisson操作redis
@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锁原理
2.2示例代码
@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来实现分布式锁。