前段时间写一个项目,有一个功能实时性要求比较高,而且读写操作也很频繁,最后决定逻辑层使用Redis支持,当流程结束后再做数据持久化。因为高并发,且该功能数据比较独立,所以采用了一个单独的Redis数据源,跟主流程的Redis分开。
这里简单写一个SpringBoot配置Redis多数据源的Demo记录一下。
pom.xml 文件引入Redis依赖:spring-boot-starter-data-redis 、线程池:commons-pool2 :
<dependencies> ... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.1.3.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.9.0</version> </dependency> ... </dependencies>
既然是多数据源,就需要配置两个Redis数据源。在 application.yml 或 application.properties 添加配置,本篇使用 application.yml 文件:
1: 配置一个端口为6379的默认Redis;
2: 配置一个端口为6380的 UserRedis;连接池的配置写在上面和下面都行,初始化的时候能读取到就行了
spring: redis: host: localhost port: 6379 timeout: 3000 database: 0 # 第二个redis配置 redis-user: # Host host: localhost # 密码 password: # 端口 port: 6380 # 超时时间 timeout: 3000 # 数据库 database: 0 # 连接池最大活跃连接数 max-active: 100 # 连接池最大建立连接等待时间, 单位为ms, 如果超过此时间将抛出异常 max-wait: 3000 # 连接池最大空闲连接数, 超过空闲数将被标记为不可用,然后被释放 max-idle: 20 # 连接池里始终应该保持的最小连接数 min-idle: 0
因为这里拟定第二个Redis数据源是为User逻辑服务的,所以创建一个读取UserRedis配置类:
UserRedisProperties.java :
package com.demo.redisdemo.config.redis; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @author AnYuan */ @Data @Component // 配置前缀 @ConfigurationProperties( prefix = "spring.redis-user") public class UserRedisProperties { /** * 配置里面的中划线: '-' 转为驼峰写法即可读取配置 */ private String host; private String password; private int port; private int timeout; private int database; private int maxWait; private int maxActive; private int maxIdle; private int minIdle; }
接下来就是重点,总结来说就是读取UserRedis的配置,声明一个 @Bean ,设置好对应的参数,创建第二个RedisTemplate。
创建一个初始化的配置类:RedisConfigure.java :
package com.demo.redisdemo.config.redis; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.apache.logging.log4j.util.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; 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.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * Redis 初始化配置 * @author AnYuan */ @Configuration @EnableCaching public class RedisConfigure extends CachingConfigurerSupport { @Autowired private UserRedisProperties userRedisProperties; /** * @Bean: * 1: 这里声明该方法返回的是受Spring容器管理的Bean * 2: 方法名与返回类名一致,但首字母小写:为redisTemplateUser */ @Bean public <T> RedisTemplate<String, T> redisTemplateUser() { // 基本配置 RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); configuration.setHostName(userRedisProperties.getHost()); configuration.setPort(userRedisProperties.getPort()); configuration.setDatabase(userRedisProperties.getDatabase()); if (Strings.isNotBlank(userRedisProperties.getPassword())) { configuration.setPassword(RedisPassword.of(userRedisProperties.getPassword())); } // 连接池配置 GenericObjectPoolConfig<Object> genericObjectPoolConfig = new GenericObjectPoolConfig<>(); genericObjectPoolConfig.setMaxTotal(userRedisProperties.getMaxActive()); genericObjectPoolConfig.setMaxWaitMillis(userRedisProperties.getMaxWait()); genericObjectPoolConfig.setMaxIdle(userRedisProperties.getMaxIdle()); genericObjectPoolConfig.setMinIdle(userRedisProperties.getMinIdle()); // lettuce pool LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder(); builder.poolConfig(genericObjectPoolConfig); builder.commandTimeout(Duration.ofSeconds(userRedisProperties.getTimeout())); LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(configuration, builder.build()); lettuceConnectionFactory.afterPropertiesSet(); return createRedisTemplate(lettuceConnectionFactory); } private <T> RedisTemplate<String, T> createRedisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, T> stringObjectRedisTemplate = new RedisTemplate<>(); stringObjectRedisTemplate.setConnectionFactory(redisConnectionFactory); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); // key序列化 stringObjectRedisTemplate.setKeySerializer(redisSerializer); // value序列化 stringObjectRedisTemplate.setValueSerializer(redisSerializer); // value hashMap序列化 stringObjectRedisTemplate.setHashValueSerializer(redisSerializer); // key haspMap序列化 stringObjectRedisTemplate.setHashKeySerializer(redisSerializer); return stringObjectRedisTemplate; } }
这个时候启动SpringBoot应用,Spring的容器里面就管理着一个name为redisTemplateUser的Bean了,也就是通过这个Bean可以使用第二个数据源的Redis。
最后我们做一个单元测试,创建一个测试类:RedisTest.java :
package com.demo.redisdemo.redis; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import java.util.concurrent.TimeUnit; @SpringBootTest public class RedisTest { /** * 当一个接口有多个实现类的时候 * 将 @Qualifier 注解与我们想要使用的 Spring Bean 名称一起进行装配 * Spring 框架就能从多个相同类型并满足装配要求的 Bean 中找到我们想要的 * * 这里绑定初始化里面注入的Bean:redisTemplateUser */ /** * 第二个数据源的配置并指定String数据类型 UserRedis */ @Autowired @Qualifier("redisTemplateUser") private RedisTemplate<String, String> redisTemplateUser; /** * 默认数据源配置并指定String数据类型的Redis */ @Autowired private RedisTemplate<String, String> redisTemplate; /** * 默认数据源配置的String数据类型的Redis */ @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void redisTest() { System.out.println("----------Start----------"); // 端口6380的Redis redisTemplateUser.opsForValue().set("redisTemplateUser", "success", 1, TimeUnit.DAYS); // 端口6379默认Redis redisTemplate.opsForValue().set("redisTemplate", "success", 1, TimeUnit.DAYS); // 端口6379并指定String数据类型的默认Redis stringRedisTemplate.opsForValue().set("stringRedisTemplate", "success", 1, TimeUnit.DAYS); System.out.println("----------End----------"); } }
最后开启两个Redis,当前无数据:
运行测试用例后,再查看Redis的缓存情况:
整个过程比较简单,就是引入扩展包,配置多数据源,读取数据源,初始化Bean,最后就可以直接使用了。需要注意的是:因为Redis有默认的实现类了,所以在装配使用的时候,要加上@Qualifier注解并指定前面Bean注入的名字,不然自动注入后会使用默认的配置,不能使用指定的Redis数据源。
本篇代码Github:https://github.com/Journeyerr/cnblogs/tree/master/redisDemo
Redis扩展包:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis
线程池扩展包:https://mvnrepository.com/artifact/org.apache.commons/commons-pool2