核心代码段
提供一个JedisConnectionFactory 根据配置来判断 单点 集群 还是哨兵
@Bean
@ConditionalOnMissingBean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = null;
String[] split = node.split(",");
Set<HostAndPort> nodes = new LinkedHashSet<>();
for (int i = 0; i < split.length; i++) {
try {
String[] split1 = split[i].split(":");
nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出现配置错误!请确认node=[%s]是否正确", node));
}
}
//获得默认的连接池构造器(怎么设计的,为什么不抽象出单独类,供用户使用呢) 有毒
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
//指定jedisPoolConifig来修改默认的连接池构造器(真麻烦,滥用设计模式!) !!!!
jpcb.poolConfig(jedisPoolConfig);
//通过构造器来构造jedis客户端配置
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
//如果是哨兵的模式
if (!StringUtils.isEmpty(sentinel)) {
logger.info("Redis use SentinelConfiguration");
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
String[] sentinelArray = sentinel.split(",");
for (String s : sentinelArray) {
try {
String[] split1 = s.split(":");
redisSentinelConfiguration.addSentinel(new RedisNode(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出现配置错误!请确认node=[%s]是否正确", node));
}
}
factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
}
//如果是单个节点 用Standalone模式
else if (nodes.size() == 1) {
logger.info("Redis use RedisStandaloneConfiguration");
for (HostAndPort n : nodes) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
if (!StringUtils.isEmpty(password)) {
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
}
redisStandaloneConfiguration.setPort(n.getPort());
redisStandaloneConfiguration.setHostName(n.getHost());
factory = new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
}
//集群配置信息实现
else {
logger.info("Redis use RedisStandaloneConfiguration");
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
nodes.forEach(n -> {
redisClusterConfiguration.addClusterNode(new RedisNode(n.getHost(), n.getPort()));
});
if (!StringUtils.isEmpty(password)) {
redisClusterConfiguration.setPassword(RedisPassword.of(password));
}
redisClusterConfiguration.setMaxRedirects(maxRedirect);
factory = new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
}
return factory;
}
Configration
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.*;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig;
import java.util.LinkedHashSet;
import java.util.Set;
@Configuration
@EnableCaching
public class SelfAdaptionRedisConfig<K, V> extends CachingConfigurerSupport {
private final static Logger logger = LoggerFactory.getLogger(SelfAdaptionRedisConfig.class);
@Value("${spring.redis.node}")
private String node;
@Value("${spring.redis.timeout:0}")
private int timeout;
@Value("${spring.redis.password:}")
private String password;
@Value("${spring.redis.sentinel:}")
private String sentinel;
@Value("${spring.redis.jedis.pool.max-total:8}")
private int maxTotal;
@Value("${spring.redis.jedis.pool.max-idle:8}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle:0}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-wait:-1}")
private long maxWaitMillis;
@Value("${spring.redis.jedis.pool.test-on-borrow:true}")
private boolean testOnBorrow;
@Value("${spring.redis.jedis.factory.max-redirects:5}")
private int maxRedirect;
@Autowired
private JedisPoolConfig jedisPoolConfig;
@Autowired
private JedisConnectionFactory jedisConnectionFactory;
@Bean
@ConditionalOnMissingBean
@Override
public CacheManager cacheManager() {
// 初始化缓存管理器,在这里我们可以缓存的整体过期时间什么的,我这里默认没有配置
logger.info("初始化 -> [{}]", "CacheManager RedisCacheManager Start");
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(jedisConnectionFactory);
return builder.build();
}
@Bean
@ConditionalOnMissingBean
@Override
public CacheErrorHandler errorHandler() {
// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
logger.info("初始化 -> [{}]", "Redis CacheErrorHandler");
CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
logger.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
logger.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
logger.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
logger.error("Redis occur handleCacheClearError:", e);
}
};
return cacheErrorHandler;
}
@Bean
@ConditionalOnMissingBean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
// 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
config.setMaxWaitMillis(maxWaitMillis);
//最小空闲连接数, 默认0
config.setMinIdle(minIdle);
// 最大空闲连接数, 默认8个
config.setMaxIdle(maxIdle);
// 最大连接数, 默认值8个
config.setMaxTotal(maxTotal);
//对拿到的connection进行validateObject校验
config.setTestOnBorrow(testOnBorrow);
return config;
}
// private JedisCluster getJedisCluster() {
// String[] split = node.split(",");
// Set<HostAndPort> nodes = new LinkedHashSet<>();
// for (int i = 0; i < split.length; i++) {
// try {
// String[] split1 = split[i].split(":");
// nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1]))));
// } catch (Exception e) {
// }
// }
// JedisCluster jedisCluster = null;
// if (StringUtils.isEmpty(password)) {
// jedisCluster = new JedisCluster(nodes, 5000, 3000, 10, jedisPoolConfig);
// } else {
// jedisCluster = new JedisCluster(nodes, 5000, 3000, 10, password, jedisPoolConfig);
// }
// }
@Bean
@ConditionalOnMissingBean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = null;
String[] split = node.split(",");
Set<HostAndPort> nodes = new LinkedHashSet<>();
for (int i = 0; i < split.length; i++) {
try {
String[] split1 = split[i].split(":");
nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出现配置错误!请确认node=[%s]是否正确", node));
}
}
//获得默认的连接池构造器(怎么设计的,为什么不抽象出单独类,供用户使用呢) 有毒
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
//指定jedisPoolConifig来修改默认的连接池构造器(真麻烦,滥用设计模式!) !!!!
jpcb.poolConfig(jedisPoolConfig);
//通过构造器来构造jedis客户端配置
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
//如果是哨兵的模式
if (!StringUtils.isEmpty(sentinel)) {
logger.info("Redis use SentinelConfiguration");
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
String[] sentinelArray = sentinel.split(",");
for (String s : sentinelArray) {
try {
String[] split1 = s.split(":");
redisSentinelConfiguration.addSentinel(new RedisNode(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出现配置错误!请确认node=[%s]是否正确", node));
}
}
factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
}
//如果是单个节点 用Standalone模式
else if (nodes.size() == 1) {
logger.info("Redis use RedisStandaloneConfiguration");
for (HostAndPort n : nodes) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
if (!StringUtils.isEmpty(password)) {
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
}
redisStandaloneConfiguration.setPort(n.getPort());
redisStandaloneConfiguration.setHostName(n.getHost());
factory = new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
}
//集群配置信息实现
else {
logger.info("Redis use RedisStandaloneConfiguration");
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
nodes.forEach(n -> {
redisClusterConfiguration.addClusterNode(new RedisNode(n.getHost(), n.getPort()));
});
if (!StringUtils.isEmpty(password)) {
redisClusterConfiguration.setPassword(RedisPassword.of(password));
}
redisClusterConfiguration.setMaxRedirects(maxRedirect);
factory = new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
}
return factory;
}
@Bean
@ConditionalOnMissingBean
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
//设置序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
RedisSerializer stringSerializer = new StringRedisSerializer();
// key序列化
redisTemplate.setKeySerializer(stringSerializer);
// value序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// Hash key序列化
redisTemplate.setHashKeySerializer(stringSerializer);
// Hash value序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${springboot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${redis.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
配置文件
1.yml配置
1.1支持 Cluster模式 集群
- node有多个redis的地址,以逗号分隔
- 如果redis没有密码直接去掉配置就可以了 yml配置
spring:
redis:
node: 127.0.0.1:1001,127.0.0.1:1002,127.0.0.1:1003
password: 123456
properties配置
spring.redis.node=127.0.0.1:1001,127.0.0.1:1002,127.0.0.1:1003
spring.redis.password=123456
1.2支持 Standalone模式 单点
- node里只有一个redis地址的时候,会自动变成单点模式 yml配置
spring:
redis:
node: 127.0.0.1:1001
password: 123456
properties配置
spring.redis.node=127.0.0.1:1001
spring.redis.password=123456
1.3支持 Sentinel模式 哨兵
- 当配置上sentinel以后就变成了哨兵模式 多个哨兵可以以逗号分割 yml配置
spring:
redis:
node: 127.0.0.1:1001
sentinel:127.0.0.1:1002,127.0.0.1:1003
password: 123456
properties配置
spring.redis.node=127.0.0.1:1001
spring.redis.sentinel:127.0.0.1:1002,127.0.0.1:1003
spring.redis.password=123456
1.4覆盖默认配置
- 如果没有配置这些信息,就会走默认配置 也可以在properties或者yml覆盖默认配置
#最大连接数, 默认值8个
spring.redis.jedis.pool.max-total=8
#最大空闲连接数, 默认8个
spring.redis.jedis.pool.max-idle=8
#最小空闲连接数, 默认0
spring.redis.jedis.pool.min-idle=0
#获取连接时的最大等待毫秒数,如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
spring.redis.jedis.pool.max-wait=-1
#对拿到的connection进行validateObject校验
spring.redis.jedis.pool.test-on-borrow=true
#集群时最大重定向个数默认5
spring.redis.jedis.factory.max-redirects=5
使用
代码使用
@Autowired
private RedisTemplate<String, Object> redisTemplate;