首页 > 其他分享 >Jedis实战

Jedis实战

时间:2024-11-19 11:40:25浏览次数:1  
标签:实战 String redis Value jedis key import Jedis

传统老牌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在这个版本已被标识将要删除,不再学习。

参考链接

标签:实战,String,redis,Value,jedis,key,import,Jedis
From: https://www.cnblogs.com/fun-seeker/p/18469532

相关文章

  • CSS快速上手:从零到项目实战
    CSS快速上手:从零到项目实战学习大纲一、初级部分CSS简介什么是CSS,它的作用是什么。CSS与HTML的关系。CSS基础语法选择器的种类(标签选择器、类选择器、ID选择器等)。属性和值的设置。文本样式字体大小、颜色、粗细。文本对齐方式。盒模型理解内容、内边距、边......
  • (分享源码)计算机毕业设计必看必学 上万套实战教程手把手教学JAVA、PHP,node.js,C++、pyth
     摘 要21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认知向理性认知提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。论文主要是对医疗门诊管理......
  • (分享源码)计算机毕业设计必看必学 上万套实战教程手把手教学JAVA、PHP,node.js,C++、pyth
     摘 要随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,校园跳蚤市场管理系统被用户普遍使用,为方便用户能够可以随时进行校园跳蚤市场管理系统的数据信息管理,特开发了基于spri......
  • 尚硅谷Docker实战教程学习笔记
    尚硅谷Docker实战教程学习笔记我从没想过因为即将要学习dockerfile而激动,也因这激动而顿感羞愧。————20241029目录尚硅谷Docker实战教程学习笔记写在前面1.Docker简介2.Docker安装3.Docker常用命令4.Docker镜像5.本地镜像发布到阿里云6.本地镜像发布到私有库7.Docker......
  • 鸿蒙项目实战(五):识别本地图片
    基本概念图片识码能力支持对图库中的码图进行扫描识别,并获取信息场景介绍图片识码能力支持对图库中的条形码、二维码、MULTIFUNCTIONALCODE进行识别,并获得码类型、码值、码位置信息该能力可用于一图单码和一图多码的识别,比如条形码、付款码等使用示例1、导入图片识码接......
  • odoo17 新增模块教程, 实战案例详细教程
    新增模块python3./odoo-binscaffoldgroup_send./addons会增加一个文件夹配置模块核心在于__manifest__.py#-*-coding:utf-8-*-{'name':"GroupSend",'summary':"Short(1phrase/line)summaryofthemodule'spurpose&qu......
  • Httpclient基础!!!!实战训练!!!!
    1.HttpClient1.1介绍HttpClient是ApacheJakartaCommon下的子项目,可以用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient作用:发送HTTP请求接收响应数据为什么要在Java程序中发送Http请求?有......
  • 01MybatisPlus(SpringCloud入门必学!!!!微服务!!项目实战!!深度理解MP用法!!!)
    微服务springCloud,今天第一课就是MybatisPlus!!!大家在日常开发中应该能发现,单表的CRUD功能代码重复度很高,也没有什么难度。而这部分代码量往往比较大,开发起来比较费时。        因此,目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的......
  • 大模型实战(二):langchain+Ollama调用本地大模型实现RAG(保姆级)
    文章目录一、任务描述1.环境2.功能二、代码拆解1.导入包2.配置本地模型3.实例化embedding模型4.导入向量化知识库5.加入提示词6.定义查询方法7.问答三、总体代码一、任务描述由于显卡仍然较为昂贵,个人笔记本的硬件条件很难带动大模型,因此我们可以调用一......
  • OpenAI Assistants API 企业级应用实战
    引言OpenAI在2023年底推出的AssistantsAPI为企业级AI应用开发提供了一个强大的新选择。与传统的ChatCompletionsAPI相比,AssistantsAPI提供了更完整的对话管理、文件处理和工具调用能力,特别适合构建复杂的企业应用。核心优势内置的对话线程管理原生的文件处理能......