Redis哨兵模式切换database
为了实现数据隔离
本文是切换redis的database
用同样的方法也可以切换不同的redis服务器或者集群
注册多个RedisTemplate
实现了BeanFactoryAware接口,会在Bean初始化阶段执行其方法
多个Redis实例也可使用这种方式
get(String topOrg) 方法可以根据topOrg去BeanFactory中获取对应的redisTemplate
topOrg可以存在ThreadLocal或者其他地方
@Component
public class RedisUtilFactory implements BeanFactoryAware {
private final static Logger logger = LoggerFactory.getLogger(RedisUtilFactory.class);
@Autowired
private RedisUtilConfig redisUtilConfig;
private static DefaultListableBeanFactory beanFactory;
/**
* get current redisTemplate
*
* @return
*/
public RedisTemplate get(String topOrg) {
String beanName = composeBeanName(topOrg);
if (beanFactory.containsBean(beanName)) {
Object bean = beanFactory.getBean(beanName);
if (bean instanceof RedisTemplate) {
return (RedisTemplate) bean;
}
}
// todo exception
logger.info("can not find redisTemplate by topOrg");
return null;
}
/**
* get current redisTemplate
*
* @return
*/
public RedisTemplate get() {
return get(ContextUtil.getTopOrg());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
logger.info("[databaseMap]" + redisUtilConfig.getDatabase().toString());
for (String topOrg : redisUtilConfig.getDatabase().keySet()) {
String beanName = composeBeanName(topOrg);
if (!listableBeanFactory.containsBean(beanName)) {
listableBeanFactory.registerSingleton(beanName, redisUtilConfig.getRedisTemplate(redisUtilConfig.getDatabase().get(topOrg)));
}
}
RedisUtilFactory.beanFactory = listableBeanFactory;
}
private String composeBeanName(String topOrg) {
return RedisTemplate.class.getSimpleName() + redisUtilConfig.getDatabase().get(topOrg);
}
}
redis配置类
package com.oneconnect.sg.config;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@EnableConfigurationProperties(RedisConfigProperties.class)
@Component
public class RedisConfigUtil {
private RedisConfigProperties redisConfigProperties;
@Autowired
JedisPoolConfig jedisPoolConfig;
RedisConfigUtil(RedisConfigProperties redisConfigProperties) {
this.redisConfigProperties = redisConfigProperties;
}
public RedisTemplate<String, Object> getRedisTemplate(int database) {
JedisClientConfiguration.JedisClientConfigurationBuilder clientConfigurationBuilder = JedisClientConfiguration.builder();
clientConfigurationBuilder.connectTimeout(Duration.ofMillis(redisConfigProperties.getTimeout()));
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder poolingClientConfigurationBuilder = clientConfigurationBuilder.usePooling();
poolingClientConfigurationBuilder.poolConfig(jedisPoolConfig);
JedisConnectionFactory jedisConnectionFactory;
if (ObjectUtils.isEmpty(redisConfigProperties.getSentinel()) || !redisConfigProperties.getSentinel().isEnabled()) {
// 单机redis初始化
jedisConnectionFactory = initJedisConnectionFactory(database, clientConfigurationBuilder);
} else {
// 哨兵redis初始化
jedisConnectionFactory = initJedisConnectFactoryWithSentinels(database, clientConfigurationBuilder);
}
// 实例化 RedisTemplate 对象
RedisTemplate<String, Object> redisTemplate = functionDomainRedisTemplate(jedisConnectionFactory);
// 不是spring自动初始化的jedisConnectionFactory和redisTemplate需要执行该方法
jedisConnectionFactory.afterPropertiesSet();
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 单机版redis初始化
*
* @param clientConfigurationBuilder
* @return
* @Param database
*/
private JedisConnectionFactory initJedisConnectionFactory(int database, JedisClientConfiguration.JedisClientConfigurationBuilder clientConfigurationBuilder) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(redisConfigProperties.getHost());
redisStandaloneConfiguration.setPort(redisConfigProperties.getPort());
redisStandaloneConfiguration.setPassword(redisConfigProperties.getPassword());
redisStandaloneConfiguration.setDatabase(database);
return new JedisConnectionFactory(redisStandaloneConfiguration, clientConfigurationBuilder.build());
}
/**
* 哨兵版redis初始化
*
* @param clientConfigurationBuilder
* @return
* @Param database
*/
private JedisConnectionFactory initJedisConnectFactoryWithSentinels(int database, JedisClientConfiguration.JedisClientConfigurationBuilder clientConfigurationBuilder) {
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration();
sentinelConfiguration.setPassword(redisConfigProperties.getPassword());
sentinelConfiguration.setDatabase(database);
RedisNode master = new RedisNode(redisConfigProperties.getHost(), redisConfigProperties.getPort());
master.setName(redisConfigProperties.getSentinel().getMaster().getName());
sentinelConfiguration.setMaster(master);
List<RedisNode> redisNodes = new ArrayList<>();
List<String> nodes = Arrays.asList(redisConfigProperties.getSentinel().getSentinels().split(";"));
nodes.forEach(s ->
redisNodes.add(new RedisNode(s.split(":")[0], NumberUtils.toInt(s.split(":")[1])))
);
sentinelConfiguration.setSentinels(redisNodes);
return new JedisConnectionFactory(sentinelConfiguration, clientConfigurationBuilder.build());
}
/**
* 实例化 RedisTemplate 对象
*
* @return RedisTemplate
*/
private RedisTemplate<String, Object> functionDomainRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// 开启事务
redisTemplate.setEnableTransactionSupport(false);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
ConfigurationProperties
package com.oneconnect.sg.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;
@ConfigurationProperties(prefix = "spring.redis")
public class RedisConfigProperties {
private String host;
private Integer port;
private String password;
private Integer timeout;
private Integer maxIdle;
private Integer maxTotal;
private Integer maxWaitMillis;
private Integer minEvictableIdleTimeMillis;
private Integer numTestsPerEvictionRun;
private long timeBetweenEvictionRunsMillis;
private boolean testOnBorrow;
private boolean testWhileIdle;
private Sentinel sentinel;
private Map<String, Integer> database = new HashMap<>();
public String getHost() {
return host;
}
public void setHost(String hostName) {
this.host = hostName;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getTimeout() {
return timeout;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
public Integer getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(Integer maxIdle) {
this.maxIdle = maxIdle;
}
public Integer getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(Integer maxTotal) {
this.maxTotal = maxTotal;
}
public Integer getMaxWaitMillis() {
return maxWaitMillis;
}
public void setMaxWaitMillis(Integer maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}
public Integer getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public Integer getNumTestsPerEvictionRun() {
return numTestsPerEvictionRun;
}
public void setNumTestsPerEvictionRun(Integer numTestsPerEvictionRun) {
this.numTestsPerEvictionRun = numTestsPerEvictionRun;
}
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public Sentinel getSentinel() {
return sentinel;
}
public void setSentinel(Sentinel sentinel) {
this.sentinel = sentinel;
}
public Map<String, Integer> getDatabase() {
return database;
}
public void setDatabase(Map<String, Integer> database) {
this.database = database;
}
/**
* Redis sentinel properties.
*/
public static class Sentinel {
private boolean enabled;
/**
* Name of the Redis server.
*/
private Master master;
/**
* Comma-separated list of "host:port" pairs.
*/
private String sentinels;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Master getMaster() {
return master;
}
public void setMaster(Master master) {
this.master = master;
}
public String getSentinels() {
return sentinels;
}
public void setSentinels(String sentinels) {
this.sentinels = sentinels;
}
}
public static class Master {
/**
* Name of the Redis server.
*/
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
连接池
package com.oneconnect.sg.config;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ObjectUtils;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Configuration
@EnableConfigurationProperties(RedisConfigProperties.class)
public class RedisPoolConfig {
private RedisConfigProperties redisConfigProperties;
RedisPoolConfig(RedisConfigProperties redisConfigProperties) {
this.redisConfigProperties = redisConfigProperties;
}
/**
* JedisPoolConfig 连接池
*
* @return JedisPoolConfig
*/
@Bean
protected JedisPoolConfig jedisPoolConfig() {
if (StringUtils.isEmpty(redisConfigProperties.getHost()) || ObjectUtils.isEmpty(redisConfigProperties.getDatabase())) {
return new JedisPoolConfig();
}
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大空闲数
jedisPoolConfig.setMaxIdle(redisConfigProperties.getMaxIdle());
// 连接池的最大数据库连接数
jedisPoolConfig.setMaxTotal(redisConfigProperties.getMaxTotal());
// 最大建立连接等待时间
jedisPoolConfig.setMaxWaitMillis(redisConfigProperties.getMaxWaitMillis());
// 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
jedisPoolConfig.setMinEvictableIdleTimeMillis(redisConfigProperties.getMinEvictableIdleTimeMillis());
// 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
jedisPoolConfig.setNumTestsPerEvictionRun(redisConfigProperties.getNumTestsPerEvictionRun());
// 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(redisConfigProperties.getTimeBetweenEvictionRunsMillis());
// 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
jedisPoolConfig.setTestOnBorrow(redisConfigProperties.isTestOnBorrow());
// 在空闲时检查有效性, 默认false
jedisPoolConfig.setTestWhileIdle(redisConfigProperties.isTestWhileIdle());
return jedisPoolConfig;
}
}
配置文件
spring:
redis:
host: x.x.x.x
#使用的数据库
database:
topOrg1: 0
topOrg2: 1
topOrg3: 2
#端口号
port: 16379
#如果有密码
password:
#客户端超时时间单位是毫秒 默认是2000
timeout: 10000
#最大空闲数
maxIdle: 300
#连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal
#maxActive: 600
#控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性
maxTotal: 300
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
maxWaitMillis: 1000
#连接的最小空闲时间 默认1800000毫秒(30分钟)
minEvictableIdleTimeMillis: 300000
#每次释放连接的最大数目,默认3
numTestsPerEvictionRun: 1024
#逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
timeBetweenEvictionRunsMillis: 30000
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
testOnBorrow: true
#在空闲时检查有效性, 默认false
testWhileIdle: true
sentinel:
enabled: true
master:
name: pubredis
sentinels: x.x.x.x:16379;x.x.x.x:16379