0


redis实战spring-boot-starter-data-redis

SpringBoot集成Redis环境搭建及配置详解_springboot redis 配置-CSDN博客

redis读写分离之lettuce_lettuce readfrom-CSDN博客

reids常用配置_clientconfigurationbuilder.readfrom-CSDN博客

连接池选择及Jedis连接池参数配置建议_分布式缓存服务 DCS

官网 Drivers :: Spring Data Redis

1 总结

1:默认整合了两种连接池,lettuce 和 jedis ,默认使用 lettuce连接池(因为支持的功能多)

2:如果配置了自定义的RedisConnectionFactory ,Spring Boot就不会自动配置 RedisConnectionFactory

2:不配置序列化器,我们是无法看懂的

2 pom

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        <!--    <version>2.5.1</version>-->
        </dependency>
         <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
          <!--  <version>2.9.0</version>-->
        </dependency>

配置jedis连接池时用

      <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

3 简单使用(单机版)

配置文件

spring:
  application:
    name: redis
  redis:
    port: 6379
    database: 0
    # host: 192.168.135.10
    host: localhost
    slaves: 127.0.0.1,127.0.0.1
    jedis:
      pool:
        max-active: 300
        max-idle: 300
        min-idle: 100
        max-wait: 5s
    lettuce:
      pool:
        max-active: 300
        max-idle: 300
        min-idle: 100
        max-wait: 5s
      shutdown-timeout: 100ms

** 直接使用,设置值**

    @Autowired
    RedisTemplate redisTemplate;
    @Test
    void contextLoads1() {
        //設置字符串 新值
        redisTemplate.opsForValue().set("ok","测试ok1");
        String ok = (String)redisTemplate.opsForValue().get("ok");
        System.out.println(ok);
    }

** 因为未配置序列化器**

默认加载哪个连接池

结论:不自定义连接池情况下,默认只会加载lettuce连接池

1:当我们配置文件配置了lettuce时,会加在配置单连接池中。

配置文件不会因为你配置 jedis 默认使用jedis连接池。

序列化器

1:redis类型:plain text 是字符串 对应序列化器 StringRedisSerializer

2:jedis默认序列化器就是 StringRedisSerializer

3:注册bean时方法名 不能随便起


import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.beans.factory.annotation.Autowired;
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.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisTemplateConfig {
    /**
     * 可以注入方式设置连接
     */
/*    @Autowired
    private RedisConnectionFactory factory;*/

    /**
     * RedisTemplate配置 改成json序列化模式用keys方法可取到值 否则取不到
     *
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        // 全局开启AutoType,不建议使用
        // ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        // 建议使用这种方式,小范围指定白名单
        ParserConfig.getGlobalInstance().addAccept("com.demo.");
        // 设置值(value)的序列化采用FastJsonRedisSerializer。
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        template.setDefaultSerializer(fastJsonRedisSerializer);
        // 设置键(key)的序列化采用StringRedisSerializer。
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
    /**
     * 可以删除
     */
    @Bean
    public ValueOperations valueOperations(RedisTemplate redisTemplate){
        return redisTemplate.opsForValue();
    }
    /**
     * 可以删除
     */
    @Bean
    public ListOperations listOperations(RedisTemplate redisTemplate){
        return redisTemplate.opsForList();
    }
    /**
     * 可以删除
     */
    @Bean
    public HashOperations hashOperations(RedisTemplate redisTemplate){
        return redisTemplate.opsForHash();
    }
}

** 方式二**

测试成功

4 自定义LettuceConnectionFactory

1:场景:配置文件中用户,密码是加密的,需要解密后 设置到连接池中。

2: 可以发现自定义配置参数会 覆盖配置文件的配置参数

4.1 单机版

import com.alibaba.fastjson.parser.ParserConfig;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
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.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration  // 配置类
public class RedisConfig  {

    /**
     *  配置 Redis 连接工厂
     *  意义: LettuceConnectionFactory 是连接 Redis 服务器的入口,它使用了 Lettuce 客户端,并且配置了连接池来提高性能和资源管理
     * @return
     */
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        // 配置 Redis 服务器的连接信息 可以读取配置文件获取
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName("localhost");
        redisStandaloneConfiguration.setPort(6379);
        // redisStandaloneConfiguration.setPassword("password"); // 取消注释以设置密码
        // 配置连接池
        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(10);       // 连接池中的最大连接数
        poolConfig.setMaxIdle(5);         // 连接池中的最大空闲连接数
        poolConfig.setMinIdle(1);         // 连接池中的最小空闲连接数
        poolConfig.setMaxWaitMillis(2000); // 连接池获取连接的最大等待时间
        // 创建一个带有连接池配置的 Lettuce 客户端配置
        LettucePoolingClientConfiguration lettucePoolingClientConfiguration =
            LettucePoolingClientConfiguration.builder()
                .poolConfig(poolConfig)
                .build();
        // 返回带有连接池配置的 Redis 连接工厂
        return new LettuceConnectionFactory(redisStandaloneConfiguration, lettucePoolingClientConfiguration);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        /*    2.设置连接工厂: 使用前面定义的 LettuceConnectionFactory。
            3.设置序列化器: 设置键和值的序列化器,这里使用 StringRedisSerializer 来将键和值序列化为字符串。
         */
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        // 全局开启AutoType,不建议使用
        // ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        // 建议使用这种方式,小范围指定白名单
        ParserConfig.getGlobalInstance().addAccept("com.demo.");
        // 设置值(value)的序列化采用FastJsonRedisSerializer。
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        template.setDefaultSerializer(fastJsonRedisSerializer);
        // 设置键(key)的序列化采用StringRedisSerializer。
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

}

最终加载的是代码中自定义配置的连接池参数,而不是配置文件中配置。

4.2 写主读从配置(readFrom属性配置)

1:读从库,写到主库

2:实时性高的,必须读主库,写主库

3:****默认读从库只会读最后一个从库(加载的最后一条从库ip)

** redis版本低 会报 **ERR unknown command 'ROLE'


配置读从库

import io.lettuce.core.ReadFrom;
import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.stereotype.Component;

/**
 *  1)MASTER:从主节点读取。
 * (2)MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica。
 * (3)REPLICA:从slave(replica)节点读取。
 * (4)REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master
 */
@Component
public class LuttuceReadFromConfig implements LettuceClientConfigurationBuilderCustomizer {
    @Override
    public void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) {
        //设置读优先读从机
        clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
    }
}

配置读从库 --自定义连接池

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        // 配置 Redis 服务器的连接信息 可以读取配置文件获取
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName("localhost");
        redisStandaloneConfiguration.setPort(6379);
        // redisStandaloneConfiguration.setPassword("password"); // 取消注释以设置密码
        // 配置连接池
        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(10);       // 连接池中的最大连接数
        poolConfig.setMaxIdle(5);         // 连接池中的最大空闲连接数
        poolConfig.setMinIdle(1);         // 连接池中的最小空闲连接数
        poolConfig.setMaxWaitMillis(2000); // 连接池获取连接的最大等待时间
        // 创建一个带有连接池配置的 Lettuce 客户端配置
        LettucePoolingClientConfiguration lettucePoolingClientConfiguration =
            LettucePoolingClientConfiguration.builder()
                //读时的策略
 /**
 *  1)MASTER:从主节点读取。
 * (2)MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica。
 * (3)REPLICA:从slave(replica)节点读取。
 * (4)REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master
 */

                .readFrom(ReadFrom.SLAVE_PREFERRED)
                .poolConfig(poolConfig)
                .build();
        // 返回带有连接池配置的 Redis 连接工厂
        return new LettuceConnectionFactory(redisStandaloneConfiguration, lettucePoolingClientConfiguration);
    }

自定义配置轮训读从库方式(线程安全)

读取redis时,默认是将主库连接也加上的,如果从库地址是两个,

import io.lettuce.core.ReadFrom;
import io.lettuce.core.models.role.RedisNodeDescription;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
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.RedisStaticMasterReplicaConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

@Slf4j
@Configuration  // 配置类
public class LettuceMSConfig {

    @Value("${spring.redis.slaves}")
    private String slaves;

    private final AtomicInteger index = new AtomicInteger(-1);
    /**
     *  配置 Redis 连接工厂
     * @return
     */
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        String mastHostName = "localhost";
        int mastPort = 6379;
        RedisStaticMasterReplicaConfiguration configuration =
            new RedisStaticMasterReplicaConfiguration(mastHostName,mastPort);
        //configuration.setPassword();
        if(StringUtils.isNotBlank(slaves)){
            String[] slaveHosts=slaves.split(",");
            for (int i=0;i<slaveHosts.length;i++){
                configuration.addNode(slaveHosts[i], 6379);
            }
        }

        // 配置连接池
        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(10);       // 连接池中的最大连接数
        poolConfig.setMaxIdle(5);         // 连接池中的最大空闲连接数
        poolConfig.setMinIdle(1);         // 连接池中的最小空闲连接数
        poolConfig.setMaxWaitMillis(2000); // 连接池获取连接的最大等待时间
        // 创建一个带有连接池配置的 Lettuce 客户端配置
        LettucePoolingClientConfiguration lettucePoolingClientConfiguration =
            LettucePoolingClientConfiguration.builder()
                //配置读redis时,获取哪个链接
                .readFrom(new ReadFrom() {
                @Override
                public List<RedisNodeDescription> select(Nodes nodes) {
                    List<RedisNodeDescription> allNodes = nodes.getNodes();
                    int ind = Math.abs(index.incrementAndGet() % allNodes.size());
                    RedisNodeDescription selected = allNodes.get(ind);
                    log.info("Selected random node {} with uri {}", ind, selected.getUri());
                    List<RedisNodeDescription> remaining = IntStream.range(0, allNodes.size())
                        .filter(i -> i != ind)
                        .mapToObj(allNodes::get).collect(Collectors.toList());
                    return Stream.concat(
                        Stream.of(selected),
                        remaining.stream()
                    ).collect(Collectors.toList());
                }
            }).commandTimeout(Duration.ofMillis(2000))
                .poolConfig(poolConfig)
                .build();
        // 返回带有连接池配置的 Redis 连接工厂
        return new LettuceConnectionFactory(configuration, lettucePoolingClientConfiguration);
    }
    
}

4.3 写主 读(主或从) 配置

默认轮训读,可以强制读主库

进程缓存/去设置变量让其强制获取第一个主库连接即可。

        <!-- 进程缓存 -->
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
           <!-- <version>2.8.6</version>-->
        </dependency>

** Caffeine缓存**

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

@Configuration
@Slf4j
public class CaffeineCacheConfig {
    @Bean
    public Cache caffeineCache() {
        log.info("缓存配置 Cache 初始化");
        return Caffeine.newBuilder()
            // 设置最后一次写入或访问后经过固定时间过期
            .expireAfterWrite(600, TimeUnit.SECONDS)
            // 初始的缓存空间大小
            .initialCapacity(1000)
            // 缓存的最大条数
            .maximumSize(100).build();
    }
}
import io.lettuce.core.ReadFrom;
import io.lettuce.core.models.role.RedisNodeDescription;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
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.RedisStaticMasterReplicaConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;

import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 配置自定义连接池,
 *  1:读 写分离
 *  2: 实时性高的强制读主库
 *  3:读从库时 轮训从库
 */
@Slf4j
@Configuration  // 配置类
public class LettuceMSConfig {
    @Autowired
    com.github.benmanes.caffeine.cache.Cache cache;
    @Value("${spring.redis.slaves}")
    private String slaves;

    private final AtomicInteger index = new AtomicInteger(-1);
    /**
     *  配置 Redis 连接工厂
     * @return
     */
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        String mastHostName = "localhost";
        int mastPort = 6379;
        RedisStaticMasterReplicaConfiguration configuration =
            new RedisStaticMasterReplicaConfiguration(mastHostName,mastPort);
        //configuration.setPassword();
        if(StringUtils.isNotBlank(slaves)){
            String[] slaveHosts=slaves.split(",");
            for (int i=0;i<slaveHosts.length;i++){
                configuration.addNode(slaveHosts[i], 6379);
            }
        }
        // 配置连接池
        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(10);       // 连接池中的最大连接数
        poolConfig.setMaxIdle(5);         // 连接池中的最大空闲连接数
        poolConfig.setMinIdle(1);         // 连接池中的最小空闲连接数
        poolConfig.setMaxWaitMillis(2000); // 连接池获取连接的最大等待时间
        // 创建一个带有连接池配置的 Lettuce 客户端配置
        LettucePoolingClientConfiguration lettucePoolingClientConfiguration =
            LettucePoolingClientConfiguration.builder()
                //配置读redis时,获取哪个链接
                .readFrom(new ReadFrom() {
                @Override
                public List<RedisNodeDescription> select(Nodes nodes) {
                   // allNodes 默认会将主redis连接放到第一个
                    List<RedisNodeDescription> allNodes = nodes.getNodes();
                    int ind = Math.abs(index.incrementAndGet() % allNodes.size());
                    //如果让读主库,获取第一个主库连接
                    Object readMastNow = cache.getIfPresent("readMastNow");
                    if(null !=readMastNow){
                        if ((Boolean)readMastNow) {
                            ind = 0;
                        }
                    }
                    RedisNodeDescription selected = allNodes.get(ind);
                    log.info("Selected random node {} with uri {}", ind, selected.getUri());
                    int finalInd = ind;
                    List<RedisNodeDescription> remaining = IntStream.range(0, allNodes.size())
                        .filter(i -> i != finalInd)
                        .mapToObj(allNodes::get).collect(Collectors.toList());
                    return Stream.concat(
                        Stream.of(selected),
                        remaining.stream()
                    ).collect(Collectors.toList());
                }
            }).commandTimeout(Duration.ofMillis(2000))
                .poolConfig(poolConfig)
                .build();
        // 返回带有连接池配置的 Redis 连接工厂
        return new LettuceConnectionFactory(configuration, lettucePoolingClientConfiguration);
    }

}

调用

    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    com.github.benmanes.caffeine.cache.Cache cache;
    @Test
    void contextLoads1() {
        //設置字符串 新值
        cache.put("readMastNow",true);
        redisTemplate.opsForValue().set("ok","测试ok1");
        String ok = (String)redisTemplate.opsForValue().get("ok");
        System.out.println(ok);
    }

4.4 哨兵模式配置

4.4 集群版

5 自定义JedisConnectionFactory

1:配置了Jedis连接池后,用的就是Jedis了。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class JedisConnectionConfig {
    @Autowired
   private Environment env;
    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        Integer port = Integer.valueOf(env.getProperty("spring.redis.port"));
        String host = env.getProperty("spring.redis.host");
        String password = env.getProperty("spring.redis.password");
        Integer database = Integer.valueOf(env.getProperty("spring.redis.database"));
        Integer maxActive = Integer.valueOf(env.getProperty("spring.redis.jedis.pool.max-active"));
        Integer maxIdle = Integer.valueOf(env.getProperty("spring.redis.jedis.pool.max-idle"));
        Integer minIdle = Integer.valueOf(env.getProperty("spring.redis.jedis.pool.min-idle"));
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        //最大空闲数
        poolConfig.setMaxIdle(maxIdle);
        //最大连接数
        poolConfig.setMaxTotal(maxActive);
        //最大等待毫秒数
        poolConfig.setMaxWaitMillis(20000);
        poolConfig.setMinIdle(minIdle);
        poolConfig.setTestOnBorrow(true);
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("server", 6379);
        config.setPort(port);
        config.setDatabase(database);
        config.setHostName(host);
        if (null != password) {
            config.setPassword(password);
        }
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config);
        jedisConnectionFactory.setPoolConfig(poolConfig);
        return jedisConnectionFactory;
    }
}
标签: redis 缓存

本文转载自: https://blog.csdn.net/weixin_44383484/article/details/141431260
版权归原作者 我的搬砖日常 所有, 如有侵权,请联系我们删除。

“redis实战spring-boot-starter-data-redis”的评论:

还没有评论