springboot双redis配置
1. 背景
springboot项目中本来用到了redis,由于业务要求,需要将数据也写到另一个redis中
2. 配置文件改动
2.1 之前redis配置*
spring.redis.database=xx
spring.redis.host=xxxx
spring.redis.port=xxx
spring.redis.password=xxx
2.2 改动后的redis配置
#原有redis配置
spring.redis.database=XX
spring.redis.host=XXX
spring.redis.port=XXX
spring.redis.password=XXXX
#双写redis配置-单点
#spring.redis2.database=XXX
#spring.redis2.host=XXXX
#spring.redis2.port=XXXX
#spring.redis2.password=XXXXX
3.代码改动
3.1 之前代码
RedisCacheConfig.java
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {
private static final Logger logger = LoggerFactory.getLogger(RedisCacheConfig.class);
private static final String HOST = "spring.redis.host";
private static final String PORT = "spring.redis.port";
private static final String DB_INDEX = "spring.redis.database";
private static final String PASSWORD = "spring.redis.password";
private static final String SENTINEL_NODES = "spring.redis.sentinel.nodes";
private static final String SENTINEL_MASTER = "spring.redis.sentinel.master";
@Value("${spring.redis.lettuce.pool.max-active}")
private int pollMaxActive;
@Value("${spring.redis.lettuce.pool.max-idle}")
private int pollMaxIdle;
@Value("${spring.redis.lettuce.pool.max-wait}")
private int pollMaxWait;
@Value("${spring.redis.lettuce.pool.min-idle}")
private int pollMinIdle;
@Value("${spring.redis.timeout}")
private int timeout;
/**
* key产生器,可以不用
*
* @return
*/
@Bean
public KeyGenerator wiselyKeyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
};
}
@Bean
public GenericObjectPoolConfig genericObjectPoolConfig() {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(pollMaxIdle);
poolConfig.setMaxTotal(pollMaxActive);
poolConfig.setMaxWaitMillis(pollMaxWait);
poolConfig.setMinIdle(pollMinIdle);
return poolConfig;
}
@Bean
public LettuceClientConfiguration lettuceClientConfiguration(GenericObjectPoolConfig genericObjectPoolConfig) {
return LettucePoolingClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(timeout))
.poolConfig(genericObjectPoolConfig)
.build();
}
@Bean
public RedisConnectionFactory redisOneConnectionFactory() {
int dbIndex = ConfigUtil.getInt(DB_INDEX, -1);
String host = ConfigUtil.getValue(HOST);
int port = ConfigUtil.getInt(PORT, -1);
String password = ConfigUtil.getValue(PASSWORD);
String sentinelNodes = ConfigUtil.getValue(SENTINEL_NODES);
String sentinelMaster = ConfigUtil.getValue(SENTINEL_MASTER);
return createLettuceConnectionFactory(dbIndex, host, port, password, sentinelNodes, sentinelMaster);
}
public LettuceConnectionFactory createLettuceConnectionFactory(int dbIndex, String host, int port, String password, String sentinelNodes, String sentinelMaster) {
if (!StringUtils.isEmpty(sentinelNodes)) {
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(sentinelMaster, getSentinelNodes(sentinelNodes));
redisSentinelConfiguration.setDatabase(dbIndex);
redisSentinelConfiguration.setPassword(password);
return new LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration(genericObjectPoolConfig()));
} else if (!StringUtils.isEmpty(host)) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(dbIndex);
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
return new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfiguration(genericObjectPoolConfig()));
} else {
logger.error("未配置redis单点或哨兵,请检查配置文件!");
return null;
}
}
private Set<String> getSentinelNodes(String sentinelNodes) {
String[] nodes = sentinelNodes.split(",");
return new HashSet<>(Arrays.asList(nodes));
}
/**
* 序列化
*
* @return
*/
@Bean(name = "redisTemplate")
public RedisTemplate<String, String> redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisOneConnectionFactory());
setSerializer(template);
template.afterPropertiesSet();
template.setEnableTransactionSupport(true);
return template;
}
private void setSerializer(StringRedisTemplate template) {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
3.2 改动之后的代码
RedisTwoCacheConfig.java
@Configuration
public class RedisTwoCacheConfig extends CachingConfigurerSupport {
private static final Logger logger = LoggerFactory.getLogger(RedisTwoCacheConfig.class);
private static final String HOST = "spring.redis2.host";
private static final String PORT = "spring.redis2.port";
private static final String DB_INDEX = "spring.redis2.database";
private static final String PASSWORD = "spring.redis2.password";
private static final String SENTINEL_NODES = "spring.redis2.sentinel.nodes";
private static final String SENTINEL_MASTER = "spring.redis2.sentinel.master";
// 复用redis1的连接池配置
@Value("${spring.redis.lettuce.pool.max-active}")
private int pollMaxActive;
@Value("${spring.redis.lettuce.pool.max-idle}")
private int pollMaxIdle;
@Value("${spring.redis.lettuce.pool.max-wait}")
private int pollMaxWait;
@Value("${spring.redis.lettuce.pool.min-idle}")
private int pollMinIdle;
@Value("${spring.redis.timeout}")
private int timeout;
@Bean
public GenericObjectPoolConfig genericObjectPoolConfig() {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(pollMaxIdle);
poolConfig.setMaxTotal(pollMaxActive);
poolConfig.setMaxWaitMillis(pollMaxWait);
poolConfig.setMinIdle(pollMinIdle);
return poolConfig;
}
@Bean
public LettuceClientConfiguration lettuceClientConfiguration(GenericObjectPoolConfig genericObjectPoolConfig) {
return LettucePoolingClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(timeout))
.poolConfig(genericObjectPoolConfig)
.build();
}
/**
* 配置redis连接工厂
*
* @return
*/
@Primary
@Bean
public RedisConnectionFactory redisTwoConnectionFactory() {
int dbIndex = ConfigUtil.getInt(DB_INDEX, -1);
String host = ConfigUtil.getValue(HOST);
int port = ConfigUtil.getInt(PORT, -1);
String password = ConfigUtil.getValue(PASSWORD);
String sentinelNodes = ConfigUtil.getValue(SENTINEL_NODES);
String sentinelMaster = ConfigUtil.getValue(SENTINEL_MASTER);
return createLettuceConnectionFactory(dbIndex, host, port, password, sentinelNodes, sentinelMaster);
}
public LettuceConnectionFactory createLettuceConnectionFactory(int dbIndex, String host, int port, String password, String sentinelNodes, String sentinelMaster) {
if (!StringUtils.isEmpty(sentinelNodes)) {
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(sentinelMaster, getSentinelNodes(sentinelNodes));
redisSentinelConfiguration.setDatabase(dbIndex);
redisSentinelConfiguration.setPassword(password);
return new LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration(genericObjectPoolConfig()));
} else if (!StringUtils.isEmpty(host)) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(dbIndex);
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
return new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfiguration(genericObjectPoolConfig()));
} else {
logger.error("未配置redis单点或哨兵,请检查配置文件!");
return null;
}
}
private Set<String> getSentinelNodes(String sentinelNodes) {
String[] nodes = sentinelNodes.split(",");
return new HashSet<>(Arrays.asList(nodes));
}
/**
* 配置redisTemplate 注入方式使用@Resource(name="") 方式注入
*
* @return
*/
@Bean(name = "redisTwoTemplate")
public RedisTemplate<String, String> redisTwoTemplate() {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisTwoConnectionFactory());
setSerializer(template);
template.afterPropertiesSet();
template.setEnableTransactionSupport(true);
return template;
}
/**
* key产生器,可以不用
*
* @return
*/
@Bean
public KeyGenerator wiselyKeyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
};
}
private void setSerializer(StringRedisTemplate template) {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
3.3 service层代码
@Component
public class XXXServiceImpl implements XXXService {
private final static Logger logger = LoggerFactory.getLogger(XXXServiceImpl.class);
@Autowired
RedisTemplate redisTemplate;
@Resource(name = "redisTwoTemplate")
RedisTemplate redisTwoTemplate;
/**
* 此处为模拟真实的方法调用
**/
public handler() {
String key = "redis"
Object valueOne = redisTemplate.get(key);
Object valueTwo = redisTwoTemplate.get(key);
}
}
4 注意点
- 第二个redisTemplate必须用Resource指明Bean名称,否则报错
- 第二个redis配置项,原则上对名称不做要求,但为了好理解,所以命名为redis2,其他保持不变
作者寄语:说实话,好久没写文章了,后面会经常写写,将工作中用到的小技巧或好的方式方法分享给大家,不喜勿喷