Springboot2(32)集成redis(jedis)

版权声明:转载请注明出处 https://blog.csdn.net/cowbin2012/article/details/86376105

源码地址

springboot2教程系列

Dubbo官网资料

redis cluster安装

在springboot中引入spring-boot-starter-data-redis依赖时,默认使用的时Lettuce,有时可能我们不想使用Lettuce而是使用Jedis来操作redis,这就需要我们在引入spring-boot-starter-data-redis依赖时做一些额外的依赖配置。

添加依赖

         <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>

		<!--使用池时需要引入-->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
		</dependency>

application.yml配置

spring:
    redis:
       timeout: 6000ms
       password:  
       cluster:
         max-redirects: 3   # 获取失败 最大重定向次数  
         nodes:
            - 10.10.2.139:7001
            - 10.10.2.139:7002
            - 10.10.2.139:7003
            - 10.10.2.139:7004
            - 10.10.2.139:7005
            - 10.10.2.139:7000
       jedis:
         pool:
           max-active: 1000             #连接池最大的连接数,若使用负值表示没有限制
           max-wait: 10s                 #连接池最大阻塞等待时间
           max-idle: 100                 #最大空闲连接数
           min-idle: 10

手动注入

JedisConnectionFacotory从Spring Data Redis 2.0开始已经不推荐直接显示设置连接的信息了,一方面为了使配置信息与建立连接工厂解耦,另一方面抽象出Standalone(RedisStandaloneConfiguration ),Sentinel和RedisCluster(RedisClusterConfiguration)三种模式的环境配置类和一个统一的jedis客户端连接配置类(用于配置连接池和SSL连接),使得我们可以更加灵活方便根据实际业务场景需要来配置连接信息。

@Configuration
public class RedisConfig extends CachingConfigurerSupport{

	@Autowired
    RedisProperties redisProperties;
   
    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(factory);
        return template;
    }
    
    @Bean(name="factory")  
    public RedisConnectionFactory factory(JedisPoolConfig jedisPoolConfig){
        RedisClusterConfiguration redisClusterConfiguration =
                new RedisClusterConfiguration();
        List<String> nodeList = redisProperties.getCluster().getNodes();
        for(String node : nodeList){
            String[] host =  node.split(":");
            redisClusterConfiguration.addClusterNode(
            new RedisNode(host[0],Integer.parseInt(host[1])));
        }

        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
                (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)
                JedisClientConfiguration.builder();
        //指定jedisPoolConifig来修改默认的连接池构造器(真麻烦,滥用设计模式!)
        jpcb.poolConfig(jedisPoolConfig);
        //通过构造器来构造jedis客户端配置
        JedisClientConfiguration jedisClientConfiguration = jpcb.build();
        return new JedisConnectionFactory(redisClusterConfiguration,jedisClientConfiguration);
    }

    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        //最大连接数
        jedisPoolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());
        //最小空闲连接数
        jedisPoolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());
       //最大空闲连接数
        jedisPoolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());
        //当池内没有可用的连接时,最大等待时间
        jedisPoolConfig.setMaxWaitMillis(
        redisProperties.getJedis().getPool().getMaxWait().getSeconds()*1000L);
        //------其他属性根据需要自行添加-------------
        return jedisPoolConfig;
    }

}

Redis作为高速缓存数据库,目前应用非常广泛。RedisTemplate是Spring提供用于操作redis数据库的一个类。
将数据存放到Redis中,以及数据读取。这里必然涉及到数据的系列化和反系列化。RedisTemplate默认的系列化类是JdkSerializationRedisSerializer,用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口。在存储内容时,除了属性的内容外还存了其它内容在里面,总长度长,且不容易阅读。
我们要求是存储的数据可以方便查看,也方便反系列化,方便读取数据。

JacksonJsonRedisSerializerGenericJackson2JsonRedisSerializer,两者都能系列化成json,但是后者会在json中加入@class属性,类的全路径包名,方便反系列化。前者如果存放了List则在反系列化的时候如果没指定TypeReference则会报错java.util.LinkedHashMap cannot be cast to 。

RedisTemplate里面定义了key,value,hashKey,haskValue等键,值的系列化器,我们可以自己方便的修改。如果没有设置则会有默认的。  JdkSerializationRedisSerializer。

pipeline

Jedis中JedisCluster是不支持pipeline操作的,如果使用了redis集群,在spring-boot-starter-data-redis中又正好用到的pipeline,那么会接收到Pipeline is currently not supported for JedisClusterConnection.这样的报错。

Lettuce中的pipeline

spring boot 2.0开始,配置spring-boot-starter-data-redis将不依赖Jedis,而是依赖Lettuce,在Lettuce中,redis cluster使用pipeline不会有问题。

让spring-data-redis也支持pipeline的思路

public Pipeline getPipeline()  {

    RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
    RedisConnection redisConnection = factory.getConnection();
    JedisClusterConnection jedisClusterConnection = (JedisClusterConnection) redisConnection;
    // 获取到原始到JedisCluster连接
    JedisCluster jedisCluster = jedisClusterConnection.getNativeConnection();
    // 通过key获取到具体的Jedis实例
    // 计算hash slot,根据特定的slot可以获取到特定的Jedis实例
    int slot = JedisClusterCRC16.getSlot(KEY);

    Field field = ReflectionUtils.findField(BinaryJedisCluster.class, null, JedisClusterConnectionHandler.class);
    field.setAccessible(true);
    JedisSlotBasedConnectionHandler jedisClusterConnectionHandler = null;
    try {
        jedisClusterConnectionHandler = (JedisSlotBasedConnectionHandler) field.get(jedisCluster);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    Jedis jedis = jedisClusterConnectionHandler.getConnectionFromSlot(slot);
    // 接下来就是pipeline操作了
    Pipeline pipeline = jedis.pipelined();
    return pipeline;

}

获取jedis客户端对应的slot

@PostConstruct
public void init()  {
    RedisConnection redisConnection = factory.getConnection();
    JedisClusterConnection jedisClusterConnection = (JedisClusterConnection) redisConnection;
    // 获取到原始到JedisCluster连接
    JedisCluster jedisCluster = jedisClusterConnection.getNativeConnection();
    Set<String> set = jedisCluster.getClusterNodes().keySet();
    for(String key :set ){
        JedisPool jedisPool = jedisCluster.getClusterNodes().get(key);
        Jedis jedis = jedisPool.getResource();

        String clusterNodesCommand = jedis.clusterNodes();
        String[] allNodes = clusterNodesCommand.split("\n");
        for (String allNode : allNodes) {
            String[] splits = allNode.split(" ");

            String hostAndPort = splits[1];
            if(splits[2].contains("myself")){
                ClusterNodeObject clusterNodeObject =
                    new ClusterNodeObject(splits[0], splits[1], 
                                          splits[2].contains("master"), splits[3],
                                          splits[7].equalsIgnoreCase("connected"), 
                                          splits.length == 9 ? splits[8] : null,jedis);
                nodelist.add(clusterNodeObject);
            }

        }
    }

}

String[] allNodes = clusterNodesCommand.split("\n");获取到的信息

e54b82fd2b5ab238906cff7fc6250a7bc66c6fec 192.168.1.163:6389 master - 0 1469600305090 31 connected 0-5460
166baa38c8ab56339c11f0446257c7a6059a219b 192.168.1.165:6389 slave 1609b090dfaaac702449b72d30b2330521ce2506 0 1469600304588 29 connected
1609b090dfaaac702449b72d30b2330521ce2506 192.168.1.163:6390 master - 0 1469600305592 29 connected 10923-16383
539627a393aa43e82ca8c16d1e935611fec4e709 192.168.1.163:6388 myself,master - 0 0 28 connected 5461-10922
d9b3738ff16e99075242b865a0b6cc137c20d502 192.168.1.165:6390 slave 539627a393aa43e82ca8c16d1e935611fec4e709 0 1469600305090 28 connected
101227d3cb13f08a47ad2afe1b348d0efc3cb3b0 192.168.1.165:6388 slave e54b82fd2b5ab238906cff7fc6250a7bc66c6fec 0 1469600304088 31 connected

cluster nodes 命令的输出有点儿复杂, 它的每一行都是由以下信息组成的:

  • 节点 ID :例如 3fc783611028b1707fd65345e763befb36454d73 。
  • ip:port :节点的 IP 地址和端口号, 例如 127.0.0.1:7000 , 其中 :0 表示的是客户端当前连接的 IP 地址和端口号。
  • flags :节点的角色(例如 master 、 slave 、 myself )以及状态(例如 fail ,等等)。
  • 如果节点是一个从节点的话, 那么跟在 flags 之后的将是主节点的节点 ID : 例如 127.0.0.1:7002 的主节点的节点 ID 就是 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 。
  • 集群最近一次向节点发送 PING 命令之后, 过去了多长时间还没接到回复。
  • 节点最近一次返回 PONG 回复的时间。
  • 节点的配置纪元(configuration epoch):详细信息请参考 Redis 集群规范 。
  • 本节点的网络连接情况:例如 connected 。
  • 节点目前包含的槽:例如 127.0.0.1:7001 目前包含号码为 5960 至 10921 的哈希槽。

猜你喜欢

转载自blog.csdn.net/cowbin2012/article/details/86376105