java连接腾讯云上的redis

腾讯云上的配置

在安全组上打开相关的端口即可

1569516112638

"来源" 就是你的目标服务器的 ip(也可以是 0.0.0.0/0) 协议端口可以用范围的写法 TCP:6379-6389

然后需将 redis 运行使用的配置文件 redis.conf 默认 bind 127.0.0.1(只允许本地访问)注释掉 (或者改为 0.0.0.0)

# bind 127.0.0.1
protected-mode no 从yes改为no

redis 连接单机和集群

依赖 pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- redis 依赖 commons-pool 这个依赖一定要添加 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
</dependency>

redis 参数的配置文件

redis.properties

#启动集群
#redis.cluster=true

########################### reids 单机配置 ##############################
#Matser 的 ip 地址
redis.host=49.235.196.22
#端口号
redis.port=6380
#如果有密码
#redis.password=
#客户端超时时间单位是毫秒 默认是 2000
redis.timeout=5000

########################### jedis单机与集群的公共配置 ##############################
#最大空闲数
redis.maxIdle=300
#连接池的最大数据库连接数。设为 0 表示无限制, 如果是 jedis 2.4 以后用 redis.maxTotal
#redis.maxActive=600
#控制一个 pool 可分配多少个 jedis 实例, 用来替换上面的 redis.maxActive, 如果是 jedis 2.4 以后用该属性
redis.maxTotal=1000
#最大建立连接等待时间。如果超过此时间将接到异常。设为 -1 表示无限制。
redis.maxWaitMillis=1000
#连接的最小空闲时间 默认 1800000 毫秒 (30 分钟)
redis.minEvictableIdleTimeMillis=300000
#每次释放连接的最大数目, 默认 3
redis.numTestsPerEvictionRun=1024
#逐出扫描的时间间隔 (毫秒) 如果为负数, 则不运行逐出线程, 默认 -1
redis.timeBetweenEvictionRunsMillis=30000
#是否在从池中取出连接前进行检验, 如果检验失败, 则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true
#在空闲时检查有效性, 默认 false
redis.testWhileIdle=true

########################### redis 集群配置 ##############################

redis.clusterNodes=49.235.196.22:7001,49.235.196.22:7002,49.235.196.22:7003,49.235.196.22:7004,49.235.196.22:7005,49.235.196.22:7006
redis.maxRedirects=3

#哨兵模式

#redis.sentinel.host1=192.168.177.128
#redis.sentinel.port1=26379

#redis.sentinel.host2=172.20.1.231

#redis.sentinel.port2=26379

application.yml

redis:
  cluster:
    true

三个 Redis 的配置类,RedisConfig:集群和单机的公共部分,SinglenRedisConfig 单机特有的配置,ClusterRedisConfig 集群特有的配置

package per.qiao.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;

/**

  • Create by IntelliJ Idea 2018.2

  • @author: qyp

  • Date: 2019-09-29 17:15
    */
    @Getter
    @Setter
    public abstract class RedisConfig {

    /**

    • 最大空闲数
      */
      public Integer maxIdle;

    /**

    • 控制一个 pool 可分配多少个 jedis 实例
      */
      public Integer maxTotal;

    /**

    • 最大建立连接等待时间。如果超过此时间将接到异常。设为 -1 表示无限制。
      */
      public Integer maxWaitMillis;

    /**

    • 连接的最小空闲时间 默认 1800000 毫秒 (30 分钟)
      */
      public Integer minEvictableIdleTimeMillis;

    /**

    • 每次释放连接的最大数目, 默认 3
      */
      public Integer numTestsPerEvictionRun;

    /**

    • 逐出扫描的时间间隔 (毫秒) 如果为负数, 则不运行逐出线程, 默认 -1
      */
      public long timeBetweenEvictionRunsMillis;

    /**

    • 是否在从池中取出连接前进行检验, 如果检验失败, 则从池中去除连接并尝试取出另一个
      */
      public boolean testOnBorrow;

    /**

    • 在空闲时检查有效性, 默认 false
      */
      public boolean testWhileIdle;

    /**

    • JedisPoolConfig 连接池
    • @return
      */
      @Bean
      public JedisPoolConfig jedisPoolConfig() {
      JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
      // 最大空闲数
      jedisPoolConfig.setMaxIdle(maxIdle);
      // 连接池的最大数据库连接数
      jedisPoolConfig.setMaxTotal(maxTotal);
      // 最大建立连接等待时间
      jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
      // 逐出连接的最小空闲时间 默认 1800000 毫秒 (30 分钟)
      jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
      // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认 3
      jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
      // 逐出扫描的时间间隔 (毫秒) 如果为负数, 则不运行逐出线程, 默认 -1
      jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
      // 是否在从池中取出连接前进行检验, 如果检验失败, 则从池中去除连接并尝试取出另一个
      jedisPoolConfig.setTestOnBorrow(testOnBorrow);
      // 在空闲时检查有效性, 默认 false
      jedisPoolConfig.setTestWhileIdle(testWhileIdle);
      return jedisPoolConfig;
      }

    /**

    • 单机构建 JedisConnectionFactory 对象
    • @param jedisPoolConfig
    • @return
      */
      public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
      return null;
      }

    /**

    • 集群 redis 构建 JedisConnectionFactory 对象

    • @param jedisPoolConfig

    • @param redisClusterConfiguration

    • @return
      */
      public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig, RedisClusterConfiguration redisClusterConfiguration) {

      return null;
      }

    /**

    • 实例化 RedisTemplate 对象
    • @return
      */
      @Bean
      public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
      RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
      initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
      return redisTemplate;
      }

    /**

    • 设置数据存入 redis 的序列化方式, 并开启事务
    • @param redisTemplate
    • @param factory
      */
      public void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
      // 如果不配置 Serializer,那么存储的时候缺省使用 String,如果用 User 类型存储,那么会提示错误 User can't cast to String!
      redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setHashKeySerializer(new StringRedisSerializer());
      redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
      redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
      // 开启事务
      //redisTemplate.setEnableTransactionSupport(true);
      redisTemplate.setConnectionFactory(factory);
      }
      }

SinglenRedisConfig.java

package per.qiao.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;

/**

  • PropertySource 加载文件到当前类上下文的 Environment 中
    */
    @Configuration
    @PropertySource("classpath:redis.properties")
    @ConfigurationProperties(prefix = "redis")
    @ConditionalOnMissingBean(ClusterRedisConfig.class)
    @Getter
    @Setter
    public class SinglenRedisConfig extends RedisConfig {

    /**

    • host,port,timeout
    • 这三个是单机属性
      */
      private String host;

    private Integer port;

    private Integer timeout;

    /**

    • 单机版配置

    • @param @param jedisPoolConfig

    • @param @return

    • @return JedisConnectionFactory

    • @throws

    • @Title: JedisConnectionFactory

    • @autor lpl

    • @date 2018 年 2 月 24 日
      */
      @Bean
      public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
      JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
      // 连接池
      redisConnectionFactory.setPoolConfig(jedisPoolConfig);
      //IP 地址
      redisConnectionFactory.setHostName(host);
      // 端口号
      redisConnectionFactory.setPort(port);
      // 如果 Redis 设置有密码
      //JedisConnectionFactory.setPassword(password);

      // 客户端超时时间单位是毫秒
      redisConnectionFactory.setTimeout(timeout);
      return redisConnectionFactory;
      }
      }

ClusterRedisConfig.java

package per.qiao.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**

  • @author qiao
    */
    @Configuration
    @PropertySource("classpath:redis.properties")
    //@EnableConfigurationProperties(ClusterRedisConfig.class)
    @ConfigurationProperties(prefix = "redis")
    //@ConditionalOnExpression("#{'true'.equals(environment['redis.cluster'])}")
    //@ConditionalOnExpression("'${redis.cluster}'=='true'")
    @ConditionalOnExpression("${redis.cluster}")
    @Getter
    @Setter
    public class ClusterRedisConfig extends RedisConfig {

    /**

    • clusterNodes,mmaxRedirectsac
    • 这两个是集群属性
      */
      private List<String> clusterNodes;

    private Integer maxRedirects;

    /**

    • Redis 集群的配置

    • @return RedisClusterConfiguration

    • @throws

    • @autor lpl

    • @date 2017 年 12 月 22 日
      */
      @Bean
      public RedisClusterConfiguration redisClusterConfiguration() {
      RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();

      Set<RedisNode> nodes = clusterNodes.stream().map(ipPort -> {
      String[] ipAndPort = ipPort.split(":");
      return new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1]));
      }).collect(Collectors.toSet());

      redisClusterConfiguration.setClusterNodes(nodes);
      redisClusterConfiguration.setMaxRedirects(maxRedirects);

      return redisClusterConfiguration;
      }

    /**

    • 配置工厂

    • @param @param jedisPoolConfig

    • @param @return

    • @return JedisConnectionFactory

    • @throws

    • @Title: JedisConnectionFactory

    • @autor lpl

    • @date 2017 年 12 月 22 日
      */
      @Bean
      public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig, RedisClusterConfiguration redisClusterConfiguration) {
      JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig);

      return redisConnectionFactory;
      }

}

测试

@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisTest {
<span class="hljs-meta">@Autowired</span>
RedisUtil redisUtil;

<span class="hljs-meta">@Test</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {
    <span class="hljs-comment">//redisUtil.del("abc");</span>
    redisUtil.set(<span class="hljs-string">"abc"</span>, <span class="hljs-string">"1234567"</span>);
    System.out.println(<span class="hljs-string">"================"</span>);
    <span class="hljs-type">Object</span> <span class="hljs-variable">myKey</span> <span class="hljs-operator">=</span> redisUtil.get(<span class="hljs-string">"abc"</span>);
    System.out.println(myKey);
}

}

遗留问题

问题 1:在集群配置上出了个问题 ClusterRedisConfig

问题一、使用@PropertySource("classpath:redis.properties")加载配置文件,然后使用@ConditionalOnExpression获取不到配置文件中的值

使用@PropertySource("classpath:redis.properties")加载指定的属性文件, 然后使用@ConfigurationProperties(prefix = "redis")指定前缀,最后使用@ConditionalOnExpression("${redis.cluster}")判断是否加载当前类

但是这个判断不能按照设想进行控制。

1. 当我将属性 redis.cluster 放到 redis.properties 中时,获取不到值,抛 there is still more data in the expression in 'lcurly({)'错误
2. 当我把属性 redis.cluster 放到 application.yml 文件中时,是可以成功的。

猜想:yml 文件最先加载,@ConditionalOnExpression又比@PropertySource先加载,所以拿不到数据

问题 2:在使用 lettuce 连接 redis 时,项目总是报集群的连接超时,但是项目可以正常使用

这是使用 lettuce(生菜) 连接的 yml 配置文件

spring:
 redis:
     database: 1
 #   host:
 #   port:
 #   password:
     cluster:
       nodes:
        - 49.235.196.22:7001
        - 49.235.196.22:7002
        - 49.235.196.22:7003
        - 49.235.196.22:7004
        - 49.235.196.22:7005
        - 49.235.196.22:7006
     lettuce:
      pool:
         # 连接池最大连接数 默认 8 ,负数表示没有限制
        max-active: 8
         # 连接池中的最大空闲连接 默认 8
        max-idle: 8
         # 连接池中的最小空闲连接 默认 0
        min-idle: 0
         # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-wait: -1s
     host: 49.235.196.22
     port: 7001
    #timeout: 30000s

这是配置类

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig2 {
<span class="hljs-comment">/**
 * springboot2.x 使用LettuceConnectionFactory 代替 RedisConnectionFactory
 * application.yml配置基本信息后,springboot2.x  RedisAutoConfiguration能够自动装配
 * LettuceConnectionFactory 和 RedisConnectionFactory 及其 RedisTemplate
 * <span class="hljs-doctag">@param</span> redisConnectionFactory
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-meta">@Bean</span>
<span class="hljs-keyword">public</span> RedisTemplate&lt;String, Object&gt; <span class="hljs-title function_">redisTemplate</span><span class="hljs-params">(LettuceConnectionFactory redisConnectionFactory)</span>{
    RedisTemplate&lt;String, Object&gt; redisTemplate = <span class="hljs-keyword">new</span> <span class="hljs-title class_">RedisTemplate</span>&lt;String, Object&gt;();
    redisTemplate.setKeySerializer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">StringRedisSerializer</span>());
    redisTemplate.setValueSerializer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">GenericJackson2JsonRedisSerializer</span>());
    redisTemplate.setHashKeySerializer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">StringRedisSerializer</span>());
    redisTemplate.setHashValueSerializer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">GenericJackson2JsonRedisSerializer</span>());
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    <span class="hljs-keyword">return</span> redisTemplate;
}

}

参考