官网:​​https://spring.io/projects/spring-data-redis​

Spring Data Redis是Spring Data系列的一部分,可轻松配置并从Spring应用程序访问Redis,并支持low-level和hight-level的抽象接口。可以看到spring data下面共提供了如下技术方案:

spring-boot整合redis(多数据源)_spring

市面上已经有 Redis、Redisson、Lettuce 等优秀的 Java Redis 工具库,为什么还要有 Spring Data Redis 呢?

  • 对于下层,Spring Data Redis 提供了统一的操作模板(RedisTemplate 类),封装了 Jedis、Lettuce 客户端库的 API 操作,来访问 Redis 数据(Spring Data Redis 底层使用的是 Jedis、Lettuce 库)。
  • 对于上层,开发者学习如何使用 Spring Data Redis 即可,而无需关心 Jedis、Lettuce 的 API 操作。甚至,未来如果我们想将 Redis 访问从 Jedis 迁移成 Lettuce 来,无需做任何的变动。
  • springboot 2.X 版本使用Lettce作为默认连接池,可以切换到jedis。

注意:目前Spring Data Redis 暂时只支持 Jedis、Lettuce 的内部封装,而 Redisson 是由 ​​redisson-spring-data​​ 来提供。

说明:jedis的使用见 ​

接下来,我们主要介绍spring-data-redis的使用(redis单机情况),对于redis cluster、redis sentinel的情况后续介绍。

1、单数据源(springboot)

1)maven:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<!--去掉对 Lettuce 的依赖,因为 Spring Boot 优先使用 Lettuce作为Redis 客户端 -->
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入 Jedis 的依赖,这样 Spring Boot 实现对 Jedis 的自动化配置 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>

说明:spring-boot-starter-data-redis中默认使用lettuce,若使用jedis库,需要将其exclude,然后手动引入jedis。

2)redis.properties:

redis.host=localhost
redis.port=7395
redis.password=123
redis.timeout=600000
# Redis 数据库号,默认为 0
redis.database=0

#最大空闲数
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

3)RedisConfig.java

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
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.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@PropertySource("classpath:redis.properties")
public class RedisConfig {

@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Value("${redis.password}")
private String pwd;
@Value("${redis.timeout}")
private Integer timeout;

@Value("${redis.maxIdle}")
private Integer maxIdle;
@Value("${redis.maxTotal}")
private Integer maxTotal;
@Value("${redis.maxWaitMillis}")
private Integer maxWaitMillis;
@Value("${redis.minEvictableIdleTimeMillis}")
private Integer minEvictableIdleTimeMillis;
@Value("${redis.numTestsPerEvictionRun}")
private Integer numTestsPerEvictionRun;
@Value("${redis.timeBetweenEvictionRunsMillis}")
private long timeBetweenEvictionRunsMillis;
@Value("${redis.testOnBorrow}")
private boolean testOnBorrow;
@Value("${redis.testWhileIdle}")
private boolean testWhileIdle;

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

/**
* 链接工厂
*/
@Bean
public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig){
JedisConnectionFactory JedisConnectionFactory = new JedisConnectionFactory();
JedisConnectionFactory.setPoolConfig(jedisPoolConfig);
JedisConnectionFactory.setHostName(host);
JedisConnectionFactory.setPort(port);
if (pwd != null && !pwd.equals("")) {
JedisConnectionFactory.setPassword(pwd);
}
JedisConnectionFactory.setTimeout(timeout);
return JedisConnectionFactory;
}

/**
* RedisTemplate
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();

StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);// key采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);// hash的key采用String的序列化方式

// GenericJackson2JsonRedisSerializer jacksonSerializer = new GenericJackson2JsonRedisSerializer();
// template.setValueSerializer(jacksonSerializer);// value采用jackson的序列化方式
// template.setHashValueSerializer(jacksonSerializer);// hash的value采用jackson序列化方式

template.setEnableTransactionSupport(true);// 开启事务
template.setConnectionFactory(factory);// 设置链接工厂

return template;
}
}

说明:

  • 如果代码中需要自己设置value的序列化方式,在RedisConfig中就无需指定。(比如:业务代码中有的按照json,有的按照pb序列化)
  • spring-data-redis中字带了string、jackson等方式的序列化

注意:如果没有指定key的序列化方式,使用时发现在redis中的key会多出来类似\xac\xed\x00\x05t\x00这种字符串,所以,最好指定key的序列化方式为string。

说明:新的spring-data-redis中,上面的方式创建JedisConnectionFactory已经被废弃了,如下:

spring-boot整合redis(多数据源)_redis_02

新的api中引入了RedisStandaloneConfiguration和JedisClientConfiguration,可以使用如下方式创建:

spring-boot整合redis(多数据源)_spring_03

4)使用:

@Service
public class TestService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;

public String test1(String value) {
redisTemplate.opsForValue().set("test_1",value);

return (String)redisTemplate.opsForValue().get("test_1");
}

public String test2(String value) {
User user = new User(99,value);
byte[] serialize = SerializeUtil.serialize(user);

redisTemplate.opsForValue().set("test_9",serialize);

byte[] bytes = (byte[])redisTemplate.opsForValue().get("test_9");
User obj = (User) SerializeUtil.unserialize(bytes);

return obj.toString();
}
}


//序列化工具
public class SerializeUtil {
public static byte[] serialize(Object object) {//序列化
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
byte[] bytes = baos.toByteArray();
return bytes;
} catch (Exception e) {
System.out.println(e);
}
return null;
}

public static Object unserialize(byte[] bytes) {//反序列化
ByteArrayInputStream bais = null;
try {
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
System.out.println(e);
}
return null;
}
}

2、单数据源2(spring)

上面使用的是注解方式(springboot默认采用config注解,来代替application.xml中各种bean的声明),接下来我们采用原始的spring application.xml配置的方式来实现一遍。

1)maven:

<properties>
<org.springframework.version>5.0.8.RELEASE</org.springframework.version>
</properties>

<dependencies>
<!-- spring mvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
</dependency>

<!-- spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.9.RELEASE</version>
<exclusions>
<exclusion><!-- 去掉对 Lettuce 的依赖-->
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>

:spring的版本要和spring-data-redis的版本对应,否则在启动应用时会报java.lang.NoSuchMethodError(spring应用报该类错误,一般来说都是版本冲突导致);其次,由于springmvc中依赖了jackson,所以jackson的版本也要对应。由于springboot简化了spring 项目的配置,所以我们可以在springboot项目中找到一份相匹配的spring、springmvc、spring-data-*、jackson等版本信息。

2)spring配置文件:

a、springMVC.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<!-- <aop:aspectj-autoproxy/> -->
<!-- 基于Annotation的映射方式 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

<!-- 自动搜索Sping的注解类 -->
<context:component-scan base-package="cn.edu.nuc.springmvc_test" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

<!-- 配置静态资源,直接映射到对应的文件夹,不被DispatcherServlet处理 -->
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/html/**" location="/html/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>

<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>

b、application.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd">

<task:annotation-driven/><!--开启spring scheduled注解-->

<!-- 自动搜索Sping的注解类-->
<context:component-scan base-package="cn.edu.nuc.springmvc_test" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!-- PropertyPlaceholderConfigurer -->
<context:property-placeholder location="classpath*:*.properties"/>

<import resource="redis.xml"/>
</beans>

c、redis.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!--redis connection pool-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" primary="true">
<property name="maxTotal" value="${redis.maxTotal}"></property>
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>

<!-- redis connection factory -->
<bean id="redisConnectionFactory" primary="true" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.password}"/>
<property name="poolConfig" ref="poolConfig"/>
</bean>

<bean id="stringReadisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

<!-- redis template -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory"/>
<property name="keySerializer" ref="stringReadisSerializer"/>
</bean>
</beans>

:多个spring配置文件中只能配置一个PropertyPlaceholderConfigurer,见:​​javascript:void(0)​

说明:在spring项目中,使用了各种xml配置代替了springboot中的各种Config类。

3)redis.properties:

在classpath下创建redis.properties文件,同上。

4)测试类:同上

 

3、单数据源(springboot中使用xml配置文件)

上面分别介绍了在springboot和spring项目中使用spring-data-redis组件。都知道springboot是用来简化spring应用的一个框架,我们可以把spring项目中的redis.xml 放到springboot项目中,并且在main方法上通过@ImportResource注解来指定redis.xml配置文件,同时在项目中去掉对应的RedisConfig类。如下:

1)main方法:

@SpringBootApplication
@ImportResource(value = {"classpath:redis.xml"})
@ComponentScan(basePackages = {"cn.edu.nuc.springboot_test.*"})
public class App {

public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(App.class, args);

}
}

2)redis.xml文件:

把上面的redis.xml放到springboot项目的classpath下,在redis.xml中通过<context:property-placeholder>标签来指定redis.properties文件,同时将RedisConfig类删掉,即可。

4、多数据源

在实际项目中,一个应用往往会从多个redis数据源中获取数据,接下来,我们介绍如何在springboot项目中配置多个redis数据源。

4.1)springboot中通过spring-data-redis配置多个redis数据源:

1)RedisConfig.java:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
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.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@PropertySource("classpath:redis.properties")
public class RedisConfig {
//数据源1
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Value("${redis.password}")
private String pwd;
@Value("${redis.timeout}")
private Integer timeout;
//数据源2
@Value("${demo.redis.host}")
private String demoHost;
@Value("${demo.redis.port}")
private Integer demoPort;
@Value("${demo.redis.password}")
private String demoPwd;
@Value("${demo.redis.database}")
private Integer demoDatabase;
@Value("${demo.redis.timeout}")
private Integer demoTimeout;
//连接池配置信息
@Value("${redis.maxIdle}")
private Integer maxIdle;
@Value("${redis.maxTotal}")
private Integer maxTotal;
@Value("${redis.maxWaitMillis}")
private Integer maxWaitMillis;
@Value("${redis.minEvictableIdleTimeMillis}")
private Integer minEvictableIdleTimeMillis;
@Value("${redis.numTestsPerEvictionRun}")
private Integer numTestsPerEvictionRun;
@Value("${redis.timeBetweenEvictionRunsMillis}")
private long timeBetweenEvictionRunsMillis;
@Value("${redis.testOnBorrow}")
private boolean testOnBorrow;
@Value("${redis.testWhileIdle}")
private boolean testWhileIdle;

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

//数据源1的链接工厂
@Bean(name="jedisConnectionFactory")
@Primary
public JedisConnectionFactory jedisConnectionFactory(@Qualifier("jedisPoolConfig") JedisPoolConfig jedisPoolConfig){
JedisConnectionFactory JedisConnectionFactory = new JedisConnectionFactory();
JedisConnectionFactory.setPoolConfig(jedisPoolConfig);
JedisConnectionFactory.setHostName(host);
JedisConnectionFactory.setPort(port);
if (pwd != null && !pwd.equals("")) {
JedisConnectionFactory.setPassword(pwd);
}
JedisConnectionFactory.setTimeout(timeout);
return JedisConnectionFactory;
}

//数据源2的连接工厂
@Bean(name="demoJedisConnectionFactory")
public JedisConnectionFactory demoJedisConnectionFactory(@Qualifier("jedisPoolConfig") JedisPoolConfig jedisPoolConfig){
//设置host、port等
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName(demoHost);
redisConfig.setPort(demoPort);
if (demoPwd != null && !demoPwd.equals("")) {
redisConfig.setPassword(RedisPassword.of(demoPwd));
}
redisConfig.setDatabase(demoDatabase);

//设置连接池、timeout等
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
.connectTimeout(Duration.ofMillis(demoTimeout))
.usePooling().poolConfig(jedisPoolConfig).build();

return new JedisConnectionFactory(redisConfig,jedisClientConfiguration);
}

//数据源1的redisTemplate
@Bean(name="redisTemplate")
public RedisTemplate<String, Object> redisTemplate(@Qualifier("jedisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();

StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);// key采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);// hash的key采用String的序列化方式
template.setEnableTransactionSupport(true);// 开启事务
template.setConnectionFactory(factory);// 设置链接工厂

return template;
}

//数据源2的redisTemplate
@Bean(name="demoRedisTemplate")
public RedisTemplate<String, Object> demoRedisTemplate(@Qualifier("demoJedisConnectionFactory") RedisConnectionFactory demoJedisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();

StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);// key采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);// hash的key采用String的序列化方式

template.setEnableTransactionSupport(true);// 开启事务
template.setConnectionFactory(demoJedisConnectionFactory);// 设置链接工厂

return template;
}
}

说明:每个数据源都配置一个连接工厂。在配置JedisConnectionFactory bean时,需要指定primary,否则会报如下错误:

Parameter 0 of method redisTemplate in org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration required a single bean, but 2 were found:

2)使用:

@Service
public class TestService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;//数据源1
@Autowired
private RedisTemplate<String, Object> demoRedisTemplate;//数据源 2

4.2)源码解读:

通过spring-data-redis使用redis,通常需要配置以下三个bean:

  • JedisPoolConfig:连接池相关的配置;
  • JedisConnectionFactory:redis连接工厂,依赖JedisPoolConfig对象;
  • RedisTemplate:redis模版,依赖JedisConnectionFactory对象;

我们知道,在spring中默认bean都是单例(除非指定bean的Scope="prototype"),在上面的多数据源配置中,JedisPoolConfig bean只配置了一个,每个数据源都配置了一个JedisConnectionFactory bean,这样会不会造成不同数据源的连接工厂使用的是一个连接池对象?先给答案:不会!!!

启动上面多数据源的应用,使用JvisualVM连接上去,通过MBean可以看到,系统中一共有两个对象池,这说明每个redis数据源的连接工厂使用了一个连接池:

spring-boot整合redis(多数据源)_redis_04

通过源码发现:

前面我们知道,spring-data-redis底层使用的是jedis或者lettuce库,这两个库都提供了redis连接池的功能。这里我们一jedis为例子:

在JedisConnectionFactory中有一个redis.clients.util.Pool pool 属性,这个属性正是jedis库提供的连接池对象(底层是org.apach.commons.pool2),该属性的初始化是通过如下方法完成的:

spring-boot整合redis(多数据源)_spring_05

从这里可以清晰的看到,我们配置的JedisPoolConfig bean只是保存了连接池相关的信息,在JedisConnectionFactory中会根据这个信息来真正创建redis的连接池。所以,JedisPoolConfig bean 是单例还是多实例,都无所谓!

4.3)xml版本的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:property-placeholder location="classpath*:redis.properties" ignore-unresolvable="true" />

<!-- 连接池信息 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" primary="true">
<property name="maxTotal" value="${redis.maxTotal}"></property>
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>

<!-- 数据源1 factory -->
<bean id="redisConnectionFactory" primary="true" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.password}"/>
<!--<property name="database" value="${redis.database}"/>-->
<property name="poolConfig" ref="poolConfig"/>
</bean>
<!-- 数据源2 factory -->
<bean id="demoRedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${demo.redis.host}"/>
<property name="port" value="${demo.redis.port}"/>
<property name="password" value="${demo.redis.password}"/>
<!--<property name="database" value="${demo.redis.database}"/>-->
<property name="poolConfig" ref="poolConfig"/>
</bean>

<bean id="stringReadisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

<!-- 数据源1 template -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory"/>
<property name="keySerializer" ref="stringReadisSerializer"/>
</bean>
<!-- 数据源2 template -->
<bean id="demoRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="demoRedisConnectionFactory"/>
<property name="keySerializer" ref="stringReadisSerializer"/>
</bean>
</beans>

注意:在springboot中,多个数据源的JedisConnectionFactory bean需要配置primary,在springmvc项目中,就不需要指定primary。