实际工作中,redis实例可能不止一个,而是多个,所以在一些项目工程中需要配置多个redis数据源,如果采用jedis、jediscluster、shardedjedis的话就不存在配置了,可以多个实例即可,在这主要说明的是springboot+redistemplate+rediscluster的配置使用。
1、首先,pom文件提供下,方便查看相关包的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zh.redis</groupId>
<artifactId>zh-redis-operation</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zh-redis-operation</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、提供下yml配置文件,IP、PORT就简写了
server:
port: 9030
servlet:
context-path: /template
spring:
news:
cluster:
nodes: ip:port,ip:port ...
max-redirects: 2
password: 123456
lettuce:
pool:
max-active: 10
max-idle: 8
max-wait: -1ms
min-idle: 0
svideo:
cluster:
nodes: ip:port,ip:port ...
max-redirects: 2
password: 123456
lettuce:
pool:
max-active: 10
max-idle: 8
max-wait: -1ms
min-idle: 0
3、接下来主要说明下核心配置代码RedisClusterProperties、SpringBootRedisConfig
package com.zh.redis.config.redis;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author : Lu Ma Ren
* @Date: 2020-09-10 15:16
* @Description: V-
*/
@SuppressWarnings("all")
@Setter
@Getter
@Component
public class RedisClusterProperties {
@Value("${spring.news.password}")
private String newsPassword;
@Value("${spring.news.cluster.nodes}")
private String newsNodes;
@Value("${spring.news.cluster.max-redirects}")
private int newsMaxRedirects;
@Value("${spring.svideo.password}")
private String svideoPassword;
@Value("${spring.svideo.cluster.nodes}")
private String svideoNodes;
@Value("${spring.svideo.cluster.max-redirects}")
private int svideoMaxRedirects;
}
package com.zh.redis.config.redis;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
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.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.ArrayList;
import java.util.List;
/**
* @author : Lu Ma Ren
* @Date: 2020-09-10 14:48
* @Description: V-
*/
@SuppressWarnings("all")
@Slf4j
@Configuration
public class SpringBootRedisConfig {
@Autowired
private RedisClusterProperties redisClusterProperties;
@Bean("newsConfig")
@ConfigurationProperties(prefix = "spring.news.lettuce.pool")
@Primary
public GenericObjectPoolConfig newsConfig() {
return new GenericObjectPoolConfig<>();
}
@Bean("svideoConfig")
@ConfigurationProperties(prefix = "spring.svideo.lettuce.pool")
public GenericObjectPoolConfig svideoConfig() {
return new GenericObjectPoolConfig<>();
}
@Bean("newsClusterConfiguration")
@ConfigurationProperties(prefix = "spring.news.cluster")
@Primary
public RedisClusterConfiguration newsClusterConfiguration() {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
redisClusterConfiguration.setMaxRedirects(redisClusterProperties.getNewsMaxRedirects());
List<RedisNode> nodeList = new ArrayList<>();
String[] cNodes = redisClusterProperties.getNewsNodes().split(",");
for (String node : cNodes) {
String[] hp = node.split(":");
nodeList.add(new RedisNode(hp[0], Integer.parseInt(hp[1])));
}
redisClusterConfiguration.setClusterNodes(nodeList);
redisClusterConfiguration.setPassword(redisClusterProperties.getNewsPassword());
return redisClusterConfiguration;
}
@Bean("svideoClusterConfiguration")
@ConfigurationProperties(prefix = "spring.svideo.cluster")
public RedisClusterConfiguration svideoClusterConfiguration() {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
redisClusterConfiguration.setMaxRedirects(redisClusterProperties.getSvideoMaxRedirects());
List<RedisNode> nodeList = new ArrayList<>();
String[] cNodes = redisClusterProperties.getSvideoNodes().split(",");
for (String node : cNodes) {
String[] hp = node.split(":");
nodeList.add(new RedisNode(hp[0], Integer.parseInt(hp[1])));
}
redisClusterConfiguration.setClusterNodes(nodeList);
redisClusterConfiguration.setPassword(redisClusterProperties.getSvideoPassword());
return redisClusterConfiguration;
}
@Bean("newsLettuceConnectionFactory")
@Primary
public LettuceConnectionFactory factory(@Qualifier("newsConfig") GenericObjectPoolConfig newsConfig,
@Qualifier("newsClusterConfiguration") RedisClusterConfiguration newsClusterConfiguration) {
LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(newsConfig).build();
return new LettuceConnectionFactory(newsClusterConfiguration, clientConfiguration);
}
@Bean("svideoLettuceConnectionFactory")
public LettuceConnectionFactory factory2(@Qualifier("svideoConfig") GenericObjectPoolConfig svideoConfig,
@Qualifier("svideoClusterConfiguration") RedisClusterConfiguration svideoClusterConfiguration) {
LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(svideoConfig).build();
return new LettuceConnectionFactory(svideoClusterConfiguration, clientConfiguration);
}
/**
* RedisTemplate 数据存入redis,需要配置序列化
*/
@Bean("newsRedisTemplate")
@Primary
public RedisTemplate<String, Object> newsRedisTemplate(@Qualifier("newsLettuceConnectionFactory") LettuceConnectionFactory newsLettuceConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(newsLettuceConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new FastJsonRedisSerializer<>(Object.class));
redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* RedisTemplate 数据存入redis,需要配置序列化
*/
@Bean("svideoRedisTemplate")
public RedisTemplate<String, Object> svideoRedisTemplate(@Qualifier("svideoLettuceConnectionFactory") LettuceConnectionFactory svideoLettuceConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(svideoLettuceConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new FastJsonRedisSerializer<>(Object.class));
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean("newsStringRedisTemplate")
@Primary
public StringRedisTemplate newsStringRedisTemplate(@Qualifier("newsLettuceConnectionFactory") LettuceConnectionFactory newsLettuceConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(newsLettuceConnectionFactory);
stringRedisTemplate.afterPropertiesSet();
return stringRedisTemplate;
}
@Bean("svideoStringRedisTemplate")
public StringRedisTemplate svideoStringRedisTemplate(@Qualifier("svideoLettuceConnectionFactory") LettuceConnectionFactory svideoLettuceConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(svideoLettuceConnectionFactory);
stringRedisTemplate.afterPropertiesSet();
return stringRedisTemplate;
}
}
我这边就展示了两个数据源的配置,以及RedisTemplate、StringRedisTemplate的配置。
4、接下来展示使用
@Autowired
@Qualifier("newsRedisTemplate")
private RedisTemplate newsRedisTemplate;
@Autowired
@Qualifier("svideoRedisTemplate")
private RedisTemplate svideoRedisTemplate;
@Autowired
@Qualifier("newsStringRedisTemplate")
private StringRedisTemplate newsStringRedisTemplate;
@Autowired
@Qualifier("svideoStringRedisTemplate")
private StringRedisTemplate svideoStringRedisTemplate;
对应的使用,就可以在代码中调用rediscluster了
总结:
1、RedisTemplate、StringRedisTemplate
StringRedisTemplate其实是继承自RedisTemplate,采用了RedisSerializer.string()进行序列化的,有疑惑的可以查看下源码,在结合SpringBootRedisConfig配置文件最后几个方法对比,就可以明白这俩的区别。
2、Autowired+Qualifier 还有Resources
Autowired和Resources,这俩的区别就是类型注入还是名称注入的问题,有兴趣度娘一堆说明,比我说的好,这块使用完全看个人习惯了,我个人倾向于Autowired+Qualifier,虽然代码多但是看着清晰
3、集群、哨兵的配置上的区别
这块的话,其实整体流程是一样的,只不过在进行配置连接工厂的时候,注入的对象不同,源码已经很明确了,在源码LettuceConnectionFactory类中,对于new LettuceConnectionFactory() 时候,有几种不同的参数,大约在代码150行上下。源代码如下:
public LettuceConnectionFactory(RedisStandaloneConfiguration standaloneConfig, LettuceClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(standaloneConfig, "RedisStandaloneConfiguration must not be null!");
this.standaloneConfig = standaloneConfig;
this.configuration = this.standaloneConfig;
}
public LettuceConnectionFactory(RedisConfiguration redisConfiguration, LettuceClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(redisConfiguration, "RedisConfiguration must not be null!");
this.configuration = redisConfiguration;
}
public LettuceConnectionFactory(RedisSentinelConfiguration sentinelConfiguration, LettuceClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(sentinelConfiguration, "RedisSentinelConfiguration must not be null!");
this.configuration = sentinelConfiguration;
}
public LettuceConnectionFactory(RedisClusterConfiguration clusterConfiguration, LettuceClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(clusterConfiguration, "RedisClusterConfiguration must not be null!");
this.configuration = clusterConfiguration;
}
4、采用原生连接
采用jedis等原生连接技术也是可以的,没有这么多复杂的文件,这个好坏不做评价,感觉跟个人习惯有关吧,我这个的业务系统采用的原生连接,因为我的业务系统需要连接redis个数不下30,有集群、单机、哨兵。千万别问为啥配置这么多,项目历史原因加上改造成本所以你懂得。
一点一点积累,总有收获的。