传统老牌Java客户端,一直在更新,支持全面的Redis命令,具有全面的API。
环境
开发工具:idea
api依赖版本:springboot 2.7.18+Jedis3.8.0
springboot1.x默认的redis客户端是Jedis,此版本的RedisTemplate是它具体实现的再封装。
springboot2.x默认的redis客户端是lettuce,此版本的RedisTemplate是它具体实现的再封装。
基础实战(单机)
1、添加依赖:在pom.xml中添加Jedis的依赖,并排除Lettuce的依赖。
<!-- 2.x 中使用jedis客户端:排除lettuce,引入jedis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2、配置文件:在application.yml或application.properties中配置Redis连接信息。
spring:
redis:
host: localhost
port: 6379
password: yourpassword
database: 0
jedis:
pool:
max-idle: 6
max-active: 10
min-idle: 2
timeout: 2000
maxWait: -1
3、配置类:创建一个配置类来配置Jedis连接工厂和JedisPool。
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private int maxWait;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
@Bean("jedisPoolConfig")
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWait(Duration.ofMillis(maxWait));
return jedisPoolConfig;
}
@Bean("jedisConnectionFactory")
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
redisStandaloneConfiguration.setDatabase(database);
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
@Bean("jedisPool")
public JedisPool jedisPool(@Qualifier("jedisPoolConfig")JedisPoolConfig jedisPoolConfig,@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory) {
return new JedisPool(jedisPoolConfig, jedisConnectionFactory.getHostName(),jedisConnectionFactory.getPort(), jedisConnectionFactory.getTimeout(), jedisConnectionFactory.getPassword(),jedisConnectionFactory.getDatabase());
}
}
4、使用Jedis:在你的服务中注入JedisPool并使用它来获取Jedis实例。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;
/**
* xx: 只有当键已经存在时才设置它。
* nx:只有当键不存在时才设置它。
* ex:设置指定的过期时间,以秒为单位。
* px:设置指定的过期时间,以毫秒为单位。
* 更多解释请查阅:redis.clients.jedis.param.SetParams。
*
* 为什么有的set方法是原子性的,有的不是原子性的?
* 在Jedis中,如果你使用set方法不带任何额外参数,它仍然是原子性的。但是,如果你尝试在不使用事务或Lua脚本的情况下组合多个操作(例如SET和EXPIRE),这就不再是原子性的了。这是因为在高并发的情况下,两个操作之间可能插入其他客户端的命令。
*/
@Component
public class JedisUtil {
private final JedisPool jedisPool;
@Autowired
public JedisUtil(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* (1)没有指定过期时间(ttl),不会自动删除。直到你显式地删除它或者Redis服务器被重启。
* (2)原子性操作
* @param key -
* @param value -
*/
public void setValue(String key, String value) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.set(key, value);
}
}
/**
* (1)指定了过期时间(ttl),过期会自动删除
* (2)不是原子性操作
* @param key -
* @param value -
* @param expire - 以秒为单位
*/
public void setValue(String key, String value,long expire){
try (Jedis jedis = jedisPool.getResource()) {
jedis.set(key,value);
jedis.expire(key,expire);
}
}
/**
* (1)完全等同于以下一组命令:SET + EXPIRE。
* (2)原子操作:set命令与expire命令组合在一起执行啦。
* @param key -
* @param value -
* @param expire - 以秒为单位
*/
public void setExValue(String key, String value,long expire){
try (Jedis jedis = jedisPool.getResource()) {
jedis.setex(key,expire,value);
}
}
/**
* (1)指定了过期时间(ttl),过期会自动删除
* (2)不是原子性操作:因为set命令与expire命令分开啦。
* @param key -
* @param value -
* @param expire -
*/
public void setNxValue(String key, String value,long expire){
try (Jedis jedis = jedisPool.getResource()) {
jedis.setnx(key,value);
jedis.expire(key,expire);
}
}
/**
* (1)指定了过期时间(ttl),过期会自动删除
* (2)原子性操作:因为set命令与expire命令分开啦。
* (3) nx: 只有当键不存在时才设置它。
* @param key -
* @param value -
* @param expire -
*/
public void setNexValue(String key, String value,long expire){
try (Jedis jedis = jedisPool.getResource()) {
jedis.set(key, value, SetParams.setParams().nx().ex(expire));
}
}
/**
* (1)指定了过期时间(ttl),过期会自动删除
* (2)原子性操作:因为set命令与expire命令分开啦。
* (3) xx: 只有当键存在时才设置它。
* @param key -
* @param value -
* @param expire -
*/
public void setNxxValue(String key, String value,long expire){
try (Jedis jedis = jedisPool.getResource()) {
jedis.set(key, value, SetParams.setParams().xx().ex(expire));
}
}
public String getValue(String key) {
try (Jedis jedis = jedisPool.getResource()) {
return jedis.get(key);
}
}
/**
* (1)没有指定过期时间(ttl),不会自动删除。直到你显式地删除它或者Redis服务器被重启。
* (2)原子性操作
* (3)对于HASH结构,redis没有提供写入hash并设置过期时间的原子操作命令。
* @param key -
* @param map -
*/
public void setHsetValue(String key,Map<String,String> map){
try (Jedis jedis = jedisPool.getResource()) {
jedis.hset(key,map);
}
}
/**
* 分批逐次读取hash表。适用于hash表比较大的场景
* count并不一定生效,Hash集合的编码由ziplist会转成dict(字典类型编码是哈希表,即hashtable)才会生效。
* count满足以下条件之一才会生效:(1)当Hash集合中的数据项(即Field-Value对)的「数目超过512」的时候。(2)当Hash集合中插入的任意一个Field-Value对中的「Value长度超过64」的时候。
* @param key -
* @return -
*/
public Map<String, String> scanHsetValue(String key){
Map<String, String> result = new HashMap<>();
try (Jedis jedis = jedisPool.getResource()) {
ScanParams scanParams = new ScanParams();
scanParams.count(100);//hash数量大于512才会生效
String cursor ="0";
do {
ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(key,cursor, scanParams);
cursor = scanResult.getCursor();
scanResult.getResult().forEach(entry->result.put(entry.getKey(),entry.getValue()));
}while (Integer.parseInt(cursor) > 0);
}
return result;
}
/**
* 一次性获取所有hash值。适用于hash表比较小的场景
* @param key -
* @return -
*/
public Map<String, String> getHsetValue(String key){
try (Jedis jedis = jedisPool.getResource()) {
return jedis.hgetAll(key);
}
}
/**
* 获取hash中某个字段值
* @param key -
* @param field -
* @return -
*/
public String getHsetValue(String key,String field){
try (Jedis jedis = jedisPool.getResource()) {
return jedis.hget(key,field);
}
}
}
其它数据结构或更多方法,请查阅源码JedisCommands接口。
5、手动使用redis事务
/**
* 手动开启事务,将多个命令放在一个事务里执行。原子性操作。
* @param key -
* @param value -
* @param second - s
*/
public void setByTran(String key,String value,long second){
try (Jedis jedis = jedisPool.getResource()) {
jedis.watch(key);//使用WATCH命令可以监控一个或多个键,如果在事务执行之前这些键被其他客户端修改,则事务将被取消。
Transaction t = jedis.multi();
t.set(key,value);
t.expire(key,second);
List<Object> results =t.exec();//返回每个命令的执行结果。事务被取消返回null。
}
}
public String getByTran(String key){
try (Jedis jedis = jedisPool.getResource()) {
jedis.watch(key);//使用WATCH命令可以监控一个或多个键,如果在事务执行之前这些键被其他客户端修改,则事务将被取消。
Transaction t= jedis.multi();
Response<String> response =t.get(key);
List<Object> results =t.exec();
return response.get();
}
}
常用的redis事务方法:
方法签名 | 描述 |
---|---|
Transaction multi() | 开启一个新的事务,并返回事务对象。 |
void set(String key, String value) | 在事务中添加一个SET命令,用于设置键值对。 |
void del(String key) | 在事务中添加一个DEL命令,用于删除给定的键。 |
void expire(String key, int seconds) | 在事务中添加一个EXPIRE命令,用于设置键的过期时间。 |
List | 执行事务中的所有命令,并返回命令的响应列表。 |
void discard() | 取消当前事务,不执行事务中的任何命令。 |
void watch(String... keys) | 使用WATCH命令监控一个或多个键,为乐观锁机制做准备。 |
void unwatch() | 取消WATCH命令对所有键的监控。 |
除了multi() 、watch()、unwatch() 在Jedis类中,其它的都在Transaction类中。事务方法一般配合这三个方法使用。
使用WATCH命令可以监控一个或多个键,如果在事务执行之前这些键被其他客户端修改,则事务将被取消。
6、使用管道(Pipeline)
适合场景:需要采用异步方式,一次发送多个指令,不同步等待其返回结果。
查阅 https://www.open-open.com/lib/view/open1410485827242.html#articleHeader2
主从复制
-
Redis主从服务搭建:
配置参考: windows配置redis集群;可编写bat脚步,注册服务,更方便管理。
注册windows服务:redis-server --service-install redis.windows-service.conf --service-name redis6379
卸载服务:redis-server --service-uninstall --service-name redis6379
开启服务:redis-server --service-start --service-name redis6379
停止服务:redis-server --service-stop --service-name redis6379 -
springboot2.x整合Redis,并使用Jedis连接池实现主从复制,读写分离
1、添加依赖:在pom.xml中添加Jedis的依赖,并排除Lettuce的依赖。
2、配置文件:在application.yml或application.properties中配置Redis连接信息。
# 禁用speing data redis的自动配置,避免与RedisTemplate某些冲突
spring:
data:
redis:
repositories:
enabled: false
# 主、从节点配置
redis:
master:
host: 127.0.0.1
port: 6379
password:
database: 0
timeout: 1000
jedis:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000
slave1:
host: 127.0.0.1
port: 6381
password:
database: 0
timeout: 1000
jedis:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000
slave2:
host: 127.0.0.1
port: 6382
password:
database: 0
timeout: 1000
jedis:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000
3、配置类:创建一个配置类来配置Jedis连接工厂和JedisPool。主服务写,从服务读
主节点配置 MasterReplicaRedisConfig.java
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
@Configuration
public class MasterReplicaRedisConfig {
@Value("${redis.master.host}")
private String host;
@Value("${redis.master.port}")
private int port;
@Value("${redis.master.password}")
private String password;
@Value("${redis.master.database}")
private int database;
@Value("${redis.master.jedis.pool.max-idle}")
private int maxIdle;
@Value("${redis.master.jedis.pool.min-idle}")
private int minIdle;
@Value("${redis.master.jedis.pool.max-wait}")
private int maxWait;
@Value("${redis.master.jedis.pool.max-active}")
private int maxActive;
@Bean("masterJedisPoolConfig")
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWait(Duration.ofMillis(maxWait));
return jedisPoolConfig;
}
@Bean("masterJedisConnectionFactory")
public JedisConnectionFactory masterJedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
redisStandaloneConfiguration.setDatabase(database);
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
@Bean("masterJedisPool")
@Primary
public JedisPool jedisPool(@Qualifier("masterJedisPoolConfig")JedisPoolConfig jedisPoolConfig,@Qualifier("masterJedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory) {
return new JedisPool(jedisPoolConfig, jedisConnectionFactory.getHostName(),jedisConnectionFactory.getPort(), jedisConnectionFactory.getTimeout(), jedisConnectionFactory.getPassword(),jedisConnectionFactory.getDatabase());
}
}
从节点配置Slave1ReplicaRedisConfig.java,其它参考此类实现
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
@Configuration
public class Slave1ReplicaRedisConfig {
@Value("${redis.slave1.host}")
private String host;
@Value("${redis.slave1.port}")
private int port;
@Value("${redis.slave1.password}")
private String password;
@Value("${redis.slave1.database}")
private int database;
@Value("${redis.slave1.jedis.pool.max-idle}")
private int maxIdle;
@Value("${redis.slave1.jedis.pool.min-idle}")
private int minIdle;
@Value("${redis.slave1.jedis.pool.max-wait}")
private int maxWait;
@Value("${redis.slave1.jedis.pool.max-active}")
private int maxActive;
@Bean("slave1JedisPoolConfig")
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWait(Duration.ofMillis(maxWait));
return jedisPoolConfig;
}
@Bean("slave1JedisConnectionFactory")
public JedisConnectionFactory slave1JedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
redisStandaloneConfiguration.setDatabase(database);
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
@Bean("slave1JedisPool")
public JedisPool jedisPool(@Qualifier("slave1JedisPoolConfig")JedisPoolConfig jedisPoolConfig,@Qualifier("slave1JedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory) {
return new JedisPool(jedisPoolConfig, jedisConnectionFactory.getHostName(),jedisConnectionFactory.getPort(), jedisConnectionFactory.getTimeout(), jedisConnectionFactory.getPassword(),jedisConnectionFactory.getDatabase());
}
}
4、使用Jedis:在你的服务中注入JedisPool并使用它来获取Jedis实例,封装工具类。
/**
* 简单主从服务,模拟主从复制、读写分离。
* 基础操作,参考单机模式。
* 若要读写分离,写操作用masterJedisPool生成Jedis实例,读操作用slave1JedisPool生成Jedis实例
*/
@Component
public class MasterSlaveReplicaJedisUtil {
private final JedisPool masterJedisPool;
private final JedisPool slave1JedisPool;
@Autowired
public MasterSlaveReplicaJedisUtil(@Qualifier("masterJedisPool") JedisPool masterJedisPool,@Qualifier("slave1JedisPool") JedisPool slave1JedisPool) {
this.masterJedisPool = masterJedisPool;
this.slave1JedisPool = slave1JedisPool;
}
/**
* 写操作,使用masterJedisPool生成jedis实例
*/
public void setValue(String key, String value) {
try (Jedis jedis = masterJedisPool.getResource()) {
jedis.set(key, value);
}
}
/**
* 读操作,使用slave1JedisPool生成jedis实例
*/
public String getValue(String key) {
try (Jedis jedis = slave1JedisPool.getResource()) {
return jedis.get(key);
}
}
}
备注:未实现负载均衡读写,只是简单的读写分离,主从复制,备份。
哨兵模式(sentinel)
-
搭建哨兵模式,参考 WINDOWS搭建哨兵模式
注意:bind、requirepass、masterauth这三个配置,需要额外留意
-
springboot2.x整合Redis,使用Jedis连接池实现主从复制,读写分离,哨兵监控
1、添加依赖:在pom.xml中添加Jedis的依赖,并排除Lettuce的依赖。
2、配置文件:在application.yml或application.properties中配置Redis连接信息
# redis 哨兵配置
spring:
redis:
sentinel:
master: mymaster
nodes: 127.0.0.1:26381,127.0.0.1:26382,127.0.0.1:26383
password: foobared
database: 0
timeout: 1000
jedis:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000
3、配置类:创建一个配置类来配置哨兵工厂jedisSentinelPool。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import java.time.Duration;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
@Configuration
public class RedisSentinelConfig {
@Value("${spring.redis.sentinel.master}")
private String master;
@Value("${spring.redis.sentinel.nodes}")
private String sentinelNodes;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWait;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxWait(Duration.ofMillis(maxWait));
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
return jedisPoolConfig;
}
@Bean
public JedisSentinelPool jedisSentinelPool(JedisPoolConfig jedisPoolConfig) {
Set<String> sentinels = Arrays.stream(sentinelNodes.split(",")).collect(Collectors.toSet());
return new JedisSentinelPool(master, sentinels, jedisPoolConfig,timeout,password,database);
}
}
4、使用Jedis:在你的服务中注入JedisPool并使用它来获取Jedis实例,封装工具类。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import redis.clients.jedis.*;
import redis.clients.jedis.params.SetParams;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* jedisSentinelPool.getResource() 获取的是主节点
* Jedis没有提供直接的命令获取从节点
*/
@Component
public class SentinelJedisUtil {
@Value("${spring.redis.sentinel.master}")
private String master;
@Value("${spring.redis.password}")
private String password;
private final JedisSentinelPool jedisSentinelPool;
@Resource(name = "sentinel0JedisPool")
private JedisPool jedisPool;
@Autowired
public SentinelJedisUtil(JedisSentinelPool jedisSentinelPool) {
this.jedisSentinelPool = jedisSentinelPool;
}
public List<Jedis> getSlaves(){
try(Jedis jedis = jedisPool.getResource()){
List<Map<String, String>> replicas= jedis.sentinelSlaves(master);
System.out.println(">>>>>>====>>>>>>");
}
return null;
}
/**
* 从主节点信息中,解析出从节点
* @return -
*/
public List<Jedis> getSlavesFromInfo(){
List<Jedis> slaves= new ArrayList<>();
try(Jedis jedis = jedisSentinelPool.getResource()){
Pattern pattern = Pattern.compile("^slave\\d+:ip=(.+),port=(\\d+),state=.+$");
String[] infos = jedis.info("replication").split("(\\r\\n)|(\\n)");
for(String info : infos) {
Matcher matcher = pattern.matcher(info);
if(matcher.find()) {
Jedis slave = new Jedis(matcher.group(1), Integer.parseInt(matcher.group(2)));
slaves.add(slave);
}
}
}
return slaves;
}
public Jedis getSlaveJedis(){
List<Jedis> slaves = getSlavesFromInfo();
return slaves.get((int)(Math.random() * slaves.size()));
}
/**
* (1)没有指定过期时间(ttl),不会自动删除。直到你显式地删除它或者Redis服务器被重启。
* (2)原子性操作
* @param key -
* @param value -
*/
public void setValue(String key, String value) {
try (Jedis jedis = jedisSentinelPool.getResource()) {
jedis.set(key, value);
}
}
public String getValue(String key) {
try(Jedis jedis = this.getSlaveJedis()){
jedis.auth(password);
return jedis.get(key);
}
}
此处需要注意,Jedis没有提供命令直接获取Jedis从节点信息,所以需要自己实现。
集群模式(cluster)
-
1 搭建集群模式
完善的高可用方案:去中心化、数据的分布式存储、读写负载均衡、主从复制、内置了类似哨兵的节点故障检测和自动故障转移功能
-
2 springboot2.x整合Redis3.X,使用Jedis客户端完成实战
-
添加依赖:在pom.xml中添加Jedis的依赖,并排除Lettuce的依赖。详细请看单机模式
-
配置文件:
# Redis 集群配置 spring: redis: cluster: nodes: 127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:26380,127.0.0.1:26381,127.0.0.1:6382 database: 0 #cluster只能是0 password: timeout: 1000 jedis: pool: max-active: 8 max-wait: -1 max-idle: 9 min-idle: 0
-
配置类
``` package com.example.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.*; import java.time.Duration; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; /** * cluster已内置连接池,有它自主管理,源码查看JedisClusterConnectionHandler类 * 通过initializeSlotsCache方法来初始化集群的槽位信息和节点信息 * 在JedisCluster中执行命令时,会通过getConnectionFromSlot方法根据键的哈希值确定要访问的槽位,然后从对应的JedisPool中获取连接。 * */ @Configuration public class RedisClusterConfig { @Value("${spring.redis.cluster.nodes}") private String sentinelNodes; @Value("${spring.redis.cluster.max-redirects}") private int maxRedirects; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.database}") private int database; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; @Value("${spring.redis.jedis.pool.max-wait}") private long maxWait; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.min-idle}") private int minIdle; @Bean public JedisPoolConfig jedisPoolConfig() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(maxActive); jedisPoolConfig.setMaxWait(Duration.ofMillis(maxWait)); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMinIdle(minIdle); return jedisPoolConfig; } @Bean public JedisCluster jedisCluster(JedisPoolConfig jedisPoolConfig) { Set<String> sentinels = Arrays.stream(sentinelNodes.split(",")).collect(Collectors.toSet()); Set<HostAndPort> nodes = new HashSet<>(); sentinels.forEach(s->{ String host = s.split(":")[0]; String port = s.split(":")[1]; nodes.add(new HostAndPort(host, Integer.parseInt(port))); }); return new JedisCluster(nodes, timeout, 2000, maxRedirects, password, jedisPoolConfig); } } ```
-
工具类
import org.springframework.stereotype.Component; import redis.clients.jedis.JedisCluster; import javax.annotation.Resource; /** * cluster已是高可用方案,读写分离没必要硬是要实现 */ @Component public class ClusterJedisUtil { @Resource private JedisCluster jedisCluster; /** * (1)没有指定过期时间(ttl),不会自动删除。直到你显式地删除它或者Redis服务器被重启。 * (2)原子性操作 * @param key - * @param value - */ public void setValue(String key, String value) { jedisCluster.set(key, value); } public void getValue(String key, String value) { jedisCluster.get(key); } }
其它操作,都可参考单机模式的实例。
-
备注
1、jedis只提供了“写入字符串+设置过期时间”的原子操作命令。若要原子操作“写入其它的数据类型+设置过期时间”,请用以下方法:(1)使用lua脚本组合成一个命令(2)使用redis的事务管理方法
2、redis的大多数命令都是原子性的,但多个命令用在一个方法时,要组合起来成一条命令,才能保证这批命令执行是原子性的。
3、实际使用上字符串类型基本满足所有使用场景,其它的场景的数据结构对于过期时间可有可无。
4、为什么使用JedisPool呢?因为单个Jedis实例不是线程安全的,在多线程环境会有奇怪错误。
5、jedis客户端操作redis主要三种模式:单台模式、分片模式(ShardedJedis)、集群模式(BinaryJedisCluster),分片模式是一种轻量级集群。ShardedJedis在这个版本已被标识将要删除,不再学习。