源码地址
springboot2教程系列
Springboot2(46)集成redis(reddisson)
redis cluster安装
Redis(1)常用操作命令Redis(2)集群redis-cluster & redis主从同步
Redis(3)内存回收原理,及内存过期淘汰策略详解
Redis(4)阿里云-开发规范
Redis(5)n种妙用,不仅仅是缓存
在springboot中引入spring-boot-starter-data-redis依赖时,默认使用的时Lettuce,有时可能我们不想使用Lettuce而是使用Jedis来操作redis,这就需要我们在引入spring-boot-starter-data-redis依赖时做一些额外的依赖配置。
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
redis.clients
jedis
org.apache.commons
commons-pool2
spring:
redis:
timeout: 6000ms
password:
cluster:
max-redirects: 3 # 获取失败 最大重定向次数
nodes:
- 10.10.2.139:7001
- 10.10.2.139:7002
- 10.10.2.139:7003
- 10.10.2.139:7004
- 10.10.2.139:7005
- 10.10.2.139:7000
jedis:
pool:
max-active: 1000 #连接池最大的连接数,若使用负值表示没有限制
max-wait: 10s #连接池最大阻塞等待时间
max-idle: 100 #最大空闲连接数
min-idle: 10
JedisConnectionFacotory从Spring Data Redis 2.0开始已经不推荐直接显示设置连接的信息了,一方面为了使配置信息与建立连接工厂解耦,另一方面抽象出Standalone(RedisStandaloneConfiguration ),Sentinel和RedisCluster(RedisClusterConfiguration)三种模式的环境配置类和一个统一的jedis客户端连接配置类(用于配置连接池和SSL连接),使得我们可以更加灵活方便根据实际业务场景需要来配置连接信息。
@Configuration
public class RedisConfig extends CachingConfigurerSupport{
@Autowired
RedisProperties redisProperties;
@Bean
public RedisTemplate<String, Serializable> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(factory);
return template;
}
@Bean(name="factory")
public RedisConnectionFactory factory(JedisPoolConfig jedisPoolConfig){
RedisClusterConfiguration redisClusterConfiguration =
new RedisClusterConfiguration();
List<String> nodeList = redisProperties.getCluster().getNodes();
for(String node : nodeList){
String[] host = node.split(":");
redisClusterConfiguration.addClusterNode(
new RedisNode(host[0],Integer.parseInt(host[1])));
}
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)
JedisClientConfiguration.builder();
//指定jedisPoolConifig来修改默认的连接池构造器(真麻烦,滥用设计模式!)
jpcb.poolConfig(jedisPoolConfig);
//通过构造器来构造jedis客户端配置
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
return new JedisConnectionFactory(redisClusterConfiguration,jedisClientConfiguration);
}
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接数
jedisPoolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());
//最小空闲连接数
jedisPoolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());
//最大空闲连接数
jedisPoolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());
//当池内没有可用的连接时,最大等待时间
jedisPoolConfig.setMaxWaitMillis(
redisProperties.getJedis().getPool().getMaxWait().getSeconds()*1000L);
//------其他属性根据需要自行添加-------------
return jedisPoolConfig;
}
}
Redis作为高速缓存数据库,目前应用非常广泛。RedisTemplate是Spring提供用于操作redis数据库的一个类。
将数据存放到Redis中,以及数据读取。这里必然涉及到数据的系列化和反系列化。RedisTemplate默认的系列化类是JdkSerializationRedisSerializer,用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口。在存储内容时,除了属性的内容外还存了其它内容在里面,总长度长,且不容易阅读。
我们要求是存储的数据可以方便查看,也方便反系列化,方便读取数据。
JacksonJsonRedisSerializer
和GenericJackson2JsonRedisSerializer
,两者都能系列化成json,但是后者会在json中加入@class属性,类的全路径包名,方便反系列化。前者如果存放了List则在反系列化的时候如果没指定TypeReference则会报错java.util.LinkedHashMap cannot be cast to 。
RedisTemplate里面定义了key,value,hashKey,haskValue等键,值的系列化器,我们可以自己方便的修改。如果没有设置则会有默认的。 JdkSerializationRedisSerializer。
Jedis中
JedisCluster
是不支持pipeline操作的,如果使用了redis集群,在spring-boot-starter-data-redis
中又正好用到的pipeline,那么会接收到Pipeline is currently not supported for JedisClusterConnection.
这样的报错。
Lettuce中的pipeline
spring boot 2.0开始,配置spring-boot-starter-data-redis
将不依赖Jedis,而是依赖Lettuce,在Lettuce中,redis cluster使用pipeline不会有问题。
让spring-data-redis也支持pipeline的思路
public Pipeline getPipeline() {
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection redisConnection = factory.getConnection();
JedisClusterConnection jedisClusterConnection = (JedisClusterConnection) redisConnection;
// 获取到原始到JedisCluster连接
JedisCluster jedisCluster = jedisClusterConnection.getNativeConnection();
// 通过key获取到具体的Jedis实例
// 计算hash slot,根据特定的slot可以获取到特定的Jedis实例
int slot = JedisClusterCRC16.getSlot(KEY);
Field field = ReflectionUtils.findField(BinaryJedisCluster.class, null, JedisClusterConnectionHandler.class);
field.setAccessible(true);
JedisSlotBasedConnectionHandler jedisClusterConnectionHandler = null;
try {
jedisClusterConnectionHandler = (JedisSlotBasedConnectionHandler) field.get(jedisCluster);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Jedis jedis = jedisClusterConnectionHandler.getConnectionFromSlot(slot);
// 接下来就是pipeline操作了
Pipeline pipeline = jedis.pipelined();
return pipeline;
}
@PostConstruct
public void init() {
RedisConnection redisConnection = factory.getConnection();
JedisClusterConnection jedisClusterConnection = (JedisClusterConnection) redisConnection;
// 获取到原始到JedisCluster连接
JedisCluster jedisCluster = jedisClusterConnection.getNativeConnection();
Set<String> set = jedisCluster.getClusterNodes().keySet();
for(String key :set ){
JedisPool jedisPool = jedisCluster.getClusterNodes().get(key);
Jedis jedis = jedisPool.getResource();
String clusterNodesCommand = jedis.clusterNodes();
String[] allNodes = clusterNodesCommand.split("\n");
for (String allNode : allNodes) {
String[] splits = allNode.split(" ");
String hostAndPort = splits[1];
if(splits[2].contains("myself")){
ClusterNodeObject clusterNodeObject =
new ClusterNodeObject(splits[0], splits[1],
splits[2].contains("master"), splits[3],
splits[7].equalsIgnoreCase("connected"),
splits.length == 9 ? splits[8] : null,jedis);
nodelist.add(clusterNodeObject);
}
}
}
}
String[] allNodes = clusterNodesCommand.split("\n");
获取到的信息
e54b82fd2b5ab238906cff7fc6250a7bc66c6fec 192.168.1.163:6389 master - 0 1469600305090 31 connected 0-5460
166baa38c8ab56339c11f0446257c7a6059a219b 192.168.1.165:6389 slave 1609b090dfaaac702449b72d30b2330521ce2506 0 1469600304588 29 connected
1609b090dfaaac702449b72d30b2330521ce2506 192.168.1.163:6390 master - 0 1469600305592 29 connected 10923-16383
539627a393aa43e82ca8c16d1e935611fec4e709 192.168.1.163:6388 myself,master - 0 0 28 connected 5461-10922
d9b3738ff16e99075242b865a0b6cc137c20d502 192.168.1.165:6390 slave 539627a393aa43e82ca8c16d1e935611fec4e709 0 1469600305090 28 connected
101227d3cb13f08a47ad2afe1b348d0efc3cb3b0 192.168.1.165:6388 slave e54b82fd2b5ab238906cff7fc6250a7bc66c6fec 0 1469600304088 31 connected
cluster nodes 命令的输出有点儿复杂, 它的每一行都是由以下信息组成的: