Redis
已经是经常使用的缓存组件了,将Redis
集成到SpringBoot
中也是比较简单的,操作时直接使用Spring
提供的RedisTemplate
即可。
Jedis
和Lettuce
是Java
操作Redis
常用的客户端。在SpringBoot 1.x 版本默认使用的是jedis
,而在SpringBoot 2.x 版本默认使用的就是Lettuce
。
Jedis
跟Lettuce
的区别如下:
-
Lettuce
和Jedis
的定位都是Redis
的client,所以他们当然都可以直接连接redis server。 -
Jedis
使用同步和阻塞IO的方式,不支持异步;lettuce
和Redisson
支持异步,底层是基于netty框架的事件驱动作为通信层。 -
Jedis
设计上就是基于线程不安全来设计,一个连接只能被一个线程使用,但是可以结合连接池来提高其性能;lettuce
基于线程安全来设计的,一个连接是被共享使用的,但是也提供了连接池,主要用于事务以及阻塞操作的命令。
1. 单机模式
单机模式连接redis,不用集群,简单配置。Redis在Springboot的自动装配中:在/META-INF/spring.factories
文件的Auto Configure
标签下面:
我们可以看一下这个自动装配类
可以看出,自动装配需要
RedisProperties.class
,这个应该比较熟悉,就是配置ip、端口、账号密码什么的,只要我们在配置文件中写了就行。
自动装配还需要一个RedisOperations实例,也就是RedisTemplate,springboot自动生成的是
RedisTemplate
和StringRedisTemplate
,我们可以自己写一个RedisTemplate
用着方便。
自动装配会自动装配
Lettuce
和Jedis
,因为自带的是lettuce的话,所以会自动装配lettuce的,我们排除lettuce包,引入jedis包,就会装配jedis的
1.1 使用Lettuce
SpringBoot 2.x默认使用的就是Lettuce
,所以比较简单:
- 导包:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
- YML配置文件:
spring:
redis:
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# Redis数据库索引(默认为0)
database: 1
# 连接超时时间(毫秒)
timeout: 3000
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认8
max-active: 20
# 连接池中的最大空闲连接 默认8
max-idle: 10
# 连接池中的最小空闲连接 默认0
min-idle: 5
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: 5000
-
RedisConfig
配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
/**
* 其实SpringBoot自动帮我们在容器中生成了一个RedisTemplate和一个StringRedisTemplate。
* 但是,这个RedisTemplate的泛型是
1.2 使用Jedis
基本和Lettuce一样,导包的时候变一下。
- 导包:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
redis.clients
jedis
- YML配置文件:
spring:
redis:
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# Redis数据库索引(默认为0)
database: 1
# 连接超时时间(毫秒)
timeout: 3000
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认8
max-active: 20
# 连接池中的最大空闲连接 默认8
max-idle: 10
# 连接池中的最小空闲连接 默认0
min-idle: 5
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: 5000
-
RedisConfig
配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
/**
* 其实SpringBoot自动帮我们在容器中生成了一个RedisTemplate和一个StringRedisTemplate。
* 但是,这个RedisTemplate的泛型是,写代码不方便,需要写好多类型转换的代码;我们需要一个泛型为形式的RedisTemplate
* 同时,设置key-value的序列化方式
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
/*
* 设置序列化参数
*/
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
//解决jackson2无法序列化LocalDateTime的问题,这里扩展一个LocalDateTime类型,它是日期类型对象 jdk1.8出的(并且这个类是不可变的和线程安全的,可以研究一下它的API),当然还需要对这个对象进行json格式的转换,如下图:
om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
1.3 RedisTemplate
的使用
RedisTemplate
是Spring提供的工具类,我们配置的时候重新设置了序列化。不管是Jedis连接还是Lettuce连接,我们都可以使用RedisTemplate
中的命令来操作数据。最常见的就是将RedisTemplate
中的方法简单封装一下,作为redis的工具类:
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* 指定失效时间
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void remove(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/**
* 获取
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 获取,泛型
* @param clazz 类型
*/
public T get(String key, Class clazz) {
ObjectMapper mapper = new ObjectMapper();
Object v=get(key);
if(v==null) return null;
T val = mapper.convertValue(v,clazz);
return val;
}
/**
* 放入
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 放入并设置时间
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean setWithExpire(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入
*
* @param key 键
* @param value 值
* @return
*/
public boolean listRightPush(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Object listRightPop(String key) {
try {
return redisTemplate.opsForList().rightPop(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
上面的例子是封装的几个方法例子,可以按照自己需要的自己添加,使用的时候直接注入RedisUtil
,然后调用方法。
2. 集群模式
对于复杂的redis方案,官方有2种:
- 一是Redis-Sentinel(哨兵模式),这是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后可以进行自动切换。
此集群方式搭建,参考:Redis集群模式1-主从复制+哨兵机制 - (jianshu.com) - 一是RedisCluster,RedisCluster是Redis的分布式解决方案,在Redis 3.0版本正式推出的,有效解决了Redis分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用Cluster架构达到负载均衡的目的。
此集群方式搭建,参考:Redis集群模式2-RedisCluster模式 - (jianshu.com)
集群模式的连接和单机差不多,只不过在yml配置文件中稍有差别,以lettuce
为例,配置文件如下:
# 1.server
server:
port: 8090
servlet:
context-path: /rediscluster
# 2. spring
spring:
redis:
cluster:
max-redirects: 3 # 获取失败 最大重定向次数
nodes:
- 192.168.100.51:6381
- 192.168.100.51:6382
- 192.168.100.51:6383
- 192.168.100.52:6381
- 192.168.100.52:6382
- 192.168.100.52:6383
- 192.168.100.53:6381
- 192.168.100.53:6382
- 192.168.100.53:6383
password: 123456
# Redis数据库索引(默认为0)
database: 0
# 连接超时时间(毫秒)
timeout: 3000
lettuce: # 若是使用jedis,将此处换成jedis即可
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认8
max-active: 300
# 连接池中的最大空闲连接 默认8
max-idle: 30
# 连接池中的最小空闲连接 默认0
min-idle: 5
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: 5000