Springboot1.5.9集成Redis(单实例与分片集群)

2023-11-11

 

springboot集成redis只需要看懂RedisAutoConfiguration类即可!

-----------------------------------惯例先上pom start-----------------------------------

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
</parent>
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

----------------------------------- end -----------------------------------

下面简单解析下RedisAutoConfiguration类源码:

从类上面的注解可以看出spring自动装配redis是基于Jedis客户端实现.

@EnableConfigurationProperties(RedisProperties.class)指明了自动装配需要的配置,其中包含了单实例、哨兵模式、分片模式的配置,可以按需灵活使用.

@Configuration
//自动装配依赖条件
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class }
//启用RedisProperties配置类,注意该注解的目的是将使用@Configuration注解的类交给spring容器管理
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {

   /**
    *  redis连接配置
    * Redis connection configuration.
    */
   @Configuration
   @ConditionalOnClass(GenericObjectPool.class)
   protected static class RedisConnectionConfiguration {
      //基础配置
      private final RedisProperties properties;
      //哨兵配置
      private final RedisSentinelConfiguration sentinelConfiguration;
      //分片配置
      private final RedisClusterConfiguration clusterConfiguration;
      //构造注入配置文件
      public RedisConnectionConfiguration(RedisProperties properties,
            ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
            ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
         this.properties = properties;
         this.sentinelConfiguration = sentinelConfiguration.getIfAvailable();
         this.clusterConfiguration = clusterConfiguration.getIfAvailable();
      }

      //如果没有RedisConnectionFactory的实例则初始化一个redis工厂实例
      @Bean
      @ConditionalOnMissingBean(RedisConnectionFactory.class)
      public JedisConnectionFactory redisConnectionFactory()
            throws UnknownHostException {
         return applyProperties(createJedisConnectionFactory());
      }
      //配置连接工厂的连接属性
      protected final JedisConnectionFactory applyProperties(
            JedisConnectionFactory factory) {
         configureConnection(factory);
         if (this.properties.isSsl()) {
            factory.setUseSsl(true);
         }
         factory.setDatabase(this.properties.getDatabase());
         if (this.properties.getTimeout() > 0) {
            factory.setTimeout(this.properties.getTimeout());
         }
         return factory;
      }

      private void configureConnection(JedisConnectionFactory factory) {
         if (StringUtils.hasText(this.properties.getUrl())) {
            configureConnectionFromUrl(factory);
         }
         else {
            factory.setHostName(this.properties.getHost());
            factory.setPort(this.properties.getPort());
            if (this.properties.getPassword() != null) {
               factory.setPassword(this.properties.getPassword());
            }
         }
      }

      private void configureConnectionFromUrl(JedisConnectionFactory factory) {
         String url = this.properties.getUrl();
         if (url.startsWith("rediss://")) {
            factory.setUseSsl(true);
         }
         try {
            URI uri = new URI(url);
            factory.setHostName(uri.getHost());
            factory.setPort(uri.getPort());
            if (uri.getUserInfo() != null) {
               String password = uri.getUserInfo();
               int index = password.lastIndexOf(":");
               if (index >= 0) {
                  password = password.substring(index + 1);
               }
               factory.setPassword(password);
            }
         }
         catch (URISyntaxException ex) {
            throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url,
                  ex);
         }
      }

      protected final RedisSentinelConfiguration getSentinelConfig() {
         if (this.sentinelConfiguration != null) {
            return this.sentinelConfiguration;
         }
         Sentinel sentinelProperties = this.properties.getSentinel();
         if (sentinelProperties != null) {
            RedisSentinelConfiguration config = new RedisSentinelConfiguration();
            config.master(sentinelProperties.getMaster());
            config.setSentinels(createSentinels(sentinelProperties));
            return config;
         }
         return null;
      }

      /**
       * Create a {@link RedisClusterConfiguration} if necessary.
       * @return {@literal null} if no cluster settings are set.
       */
      protected final RedisClusterConfiguration getClusterConfiguration() {
         if (this.clusterConfiguration != null) {
            return this.clusterConfiguration;
         }
         if (this.properties.getCluster() == null) {
            return null;
         }
         Cluster clusterProperties = this.properties.getCluster();
         RedisClusterConfiguration config = new RedisClusterConfiguration(
               clusterProperties.getNodes());

         if (clusterProperties.getMaxRedirects() != null) {
            config.setMaxRedirects(clusterProperties.getMaxRedirects());
         }
         return config;
      }
      //哨兵节点配置
      private List<RedisNode> createSentinels(Sentinel sentinel) {
         List<RedisNode> nodes = new ArrayList<RedisNode>();
         for (String node : StringUtils
               .commaDelimitedListToStringArray(sentinel.getNodes())) {
            try {
               String[] parts = StringUtils.split(node, ":");
               Assert.state(parts.length == 2, "Must be defined as 'host:port'");
               nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1])));
            }
            catch (RuntimeException ex) {
               throw new IllegalStateException(
                     "Invalid redis sentinel " + "property '" + node + "'", ex);
            }
         }
         return nodes;
      }
      //创建连接工厂
      private JedisConnectionFactory createJedisConnectionFactory() {
         JedisPoolConfig poolConfig = this.properties.getPool() != null
               ? jedisPoolConfig() : new JedisPoolConfig();
          //如果配置了哨兵,则优先启用哨兵
         if (getSentinelConfig() != null) {
            return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
         }
         //配置分片则启用分片
         if (getClusterConfiguration() != null) {
            return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
         }
         //没有配置集群则启用单实例
         return new JedisConnectionFactory(poolConfig);
      }

      private JedisPoolConfig jedisPoolConfig() {
         JedisPoolConfig config = new JedisPoolConfig();
         RedisProperties.Pool props = this.properties.getPool();
         config.setMaxTotal(props.getMaxActive());
         config.setMaxIdle(props.getMaxIdle());
         config.setMinIdle(props.getMinIdle());
         config.setMaxWaitMillis(props.getMaxWait());
         return config;
      }

   }

   /**
    * Standard Redis configuration.
    */
   @Configuration
   protected static class RedisConfiguration {
      //如果容器中没有redisTemplate<Object, Object>则创建一个该实例对象
      //我们在使用的时候需要进行增强,所以只用我们直接生成一个redisTemplate的bean,此处则不会生效
      @Bean
      @ConditionalOnMissingBean(name = "redisTemplate")
      public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                  throws UnknownHostException {
         RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
         template.setConnectionFactory(redisConnectionFactory);
         return template;
      }
      //如果容器中没有StringRedisTemplate则创建一个该实例对象
      @Bean
      @ConditionalOnMissingBean(StringRedisTemplate.class)
      public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                  throws UnknownHostException {
         StringRedisTemplate template = new StringRedisTemplate();
         template.setConnectionFactory(redisConnectionFactory);
         return template;
      }

   }

}

这个类非常容易看懂,所以实际上集成redis就变得非常简单了,下面来看看具体项目使用配置:

先看下yml配置文件,配置非常清晰,使用哪种模式按指定配置即可

spring:
  redis:
    #host: 127.0.0.1  #单实例在此配置ip,配置单实例必须屏蔽cluster max-redirects 与 nodes
    #port: 6379       #单实例在此配置端口
    password:
    timeout: 20000
    pool:
      max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
      min-idle: 0  #最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接
      max-idle: 8 #最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放
      max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
    sentinel:  #哨兵模式 适用于读多写少
      master:  #配置主节点
      nodes:   #配置从节点
    cluster:   #集群在此配置节点,配置集群必须屏蔽host与port配置
      max-redirects: 6    #最大的要重定向的次数(由于集群中数据存储在多个节点所以,在访问数据时需要通过节点进行转发)
      nodes: 192.168.210.68:6379,192.168.210.68:6380,192.168.210.68:6381,... #分片至少要6个节点才能使用

 

再来看下在代码中使用

只用注入org.springframework.data.redis.core.RedisTemplate即可通过redisTemplate操作redis

          

另外由于redisTempate会将数据序列化后存储,而序列化默认使用JdkSerializationRedisSerializer,下面简单介绍下spring-data-redis的几个序列化类:

    FastJson2JsonRedisSerializer: 序列化object对象为json字符串,存储的value值是带有双引号的,不利于反序列化

     GenericToStringSerializer: 将任何对象toString()后再序列化

      JacksonJsonRedisSerializer: 序列化object对象为json字符串,具备易读性

     Jackson2JsonRedisSerializer: 同JacksonJsonRedisSerializer,推荐使用

      JdkSerializationRedisSerializer: 序列化java对象(即只有实现了Serializable接口的对象才能被序列化),redis中存储的值不可读

     StringRedisSerializer: 用于序列化字符串,不适用于对象

 

对比后存储key统一使用StringRedisSerializer,存储value统一使用Jackson2JsonRedisSerialize最为方便,下面即是对redisTemplate进行增强(注入新的redisTemplate,spring自动注入的redisTemplate会自动失效):

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    // 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
    //序列化对象互转对象设置
    ObjectMapper om = new ObjectMapper();
    //设置任何对象的任何信息都可见  
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    //对所有非final类型的元素进行序列化
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    //设置redis存储value序列化方式
    template.setValueSerializer(jackson2JsonRedisSerializer);
    //设置redis存储key序列化方式
    template.setKeySerializer(new StringRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    //调用此方法对上面设置的信息进行初始化
    template.afterPropertiesSet();
    return template;
}
至此,springboot1.5.9集成redis结束,如有疑问,欢迎留言讨论.

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Springboot1.5.9集成Redis(单实例与分片集群) 的相关文章

随机推荐