本文使用redis-5.0.5,redis安装在/soft/redis目录下,需新建/soft/redis/data目录。
安装ruby
wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.3.tar.gz
tar -zxvf ruby-2.6.3.tar.gz
cd ruby-2.6.3/
./configure -prefix=/usr/local/ruby
make
make install
cd /usr/local/ruby/
cp bin/ruby /usr/local/bin/
cp bin/gem /usr/local/bin/
ruby --version
创建redis配置
cd /soft/redis
vim config/redis-7000.conf
port 7000
protected-mode no
daemonize yes
dir "/soft/redis/data"
dbfilename "dump-7000.rdb"
logfile "log-7000.log"
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-require-full-coverage no
配置详解
cluster-enabled yes 启动cluster集群
cluster-require-full-coverage no 有一个节点不可用,依旧运行集群
再创建其他5份配置
sed 's/7000/7001/g' config/redis-7000.conf > config/redis-7001.conf
sed 's/7000/7002/g' config/redis-7000.conf > config/redis-7002.conf
sed 's/7000/7003/g' config/redis-7000.conf > config/redis-7003.conf
sed 's/7000/7004/g' config/redis-7000.conf > config/redis-7004.conf
sed 's/7000/7005/g' config/redis-7000.conf > config/redis-7005.conf
启动这6个redis实例
redis-server config/redis-7000.conf
redis-server config/redis-7001.conf
redis-server config/redis-7002.conf
redis-server config/redis-7003.conf
redis-server config/redis-7004.conf
redis-server config/redis-7005.conf
创建集群,redis-v5版本不再使用redis-trib.rb来创建集群,使用redis-cli --cluster代替(192.168.4.176是linux服务器的ip)
redis-cli --cluster create --cluster-replicas 1 \
192.168.4.176:7000 192.168.4.176:7001 192.168.4.176:7002 192.168.4.176:7003 192.168.4.176:7004 192.168.4.176:7005
创建过程需要你输入yes确认
yes
打开一个客户端
redis-cli -p 7000
查看集群状态
cluster info
查看集群主从节点、槽位分配情况
cluster nodes
事实上新建cluster集群不是本文重点,网上有很多这种教程,cluster集群与spring boot 2集成才是本文重点。
pom.xml配置
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
其他配置省略不写了
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
redis.clients
jedis
2.10.2
org.springframework.boot
spring-boot-starter-test
spring boot 2默认使用lettuce-core作为redis的客户端。一开始我使用lettuce-core连接redis,当我kill掉一个master节点(7000节点),redis服务端能够完成主从切换,实现故障转移。spring boot 2却还是会连接7000节点,导致key落在7000节点槽位的请求一直报错。估计是lettuce-core客户端没有重建连接池缓存,所以客户端无法做到故障迁移。后来改用jedis 2.X版本,spring boot 2工程才做到了故障迁移,不去连接7000节点。注意jedis 3.X版本会导致spring-boot 2无法启动。
application.properties配置
spring.redis.timeout=2000
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-wait=1000
spring.redis.cluster.nodes=192.168.4.176:7000, 192.168.4.176:7001, 192.168.4.176:7002, 192.168.4.176:7003, 192.168.4.176:7004, 192.168.4.176:7005
# 重定向次数
spring.redis.cluster.max-redirects=5
还有一个很坑爹的地方,在application.properties写好配置后,仅仅是能连接集群,客户端也无法做到故障迁移,还需要手动写一个JedisConnectionFactory配置类,再将JedisConnectionFactory设置为RedisTemplate的配置工厂,下面 是代码。
@Configuration
public class RedisConfigJedis {
@Value("${spring.redis.timeout}")
private Integer redisTimeout;
@Value("${spring.redis.jedis.pool.max-active}")
private Integer poolMaxActive;
@Value("${spring.redis.jedis.pool.max-idle}")
private Integer poolMaxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private Integer poolMinIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private Integer poolMaxWait;
@Value("${spring.redis.cluster.nodes}")
private List clusterNodes;
@Value("${spring.redis.cluster.max-redirects}")
private Integer clusterMaxRedirects;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(poolMaxActive);
poolConfig.setMaxIdle(poolMaxIdle);
poolConfig.setMinIdle(poolMinIdle);
poolConfig.setMaxWaitMillis(poolMaxWait);
JedisClientConfiguration clientConfig = JedisClientConfiguration.builder()
.usePooling().poolConfig(poolConfig).and().readTimeout(Duration.ofMillis(redisTimeout)).build();
// cluster模式
RedisClusterConfiguration redisConfig = new RedisClusterConfiguration();
redisConfig.setMaxRedirects(clusterMaxRedirects);
for (String ipPort :clusterNodes){
String[] ipPortArr = ipPort.split(":");
redisConfig.clusterNode(ipPortArr[0], Integer.parseInt(ipPortArr[1]));
}
return new JedisConnectionFactory(redisConfig, clientConfig);
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
//key使用StringRedisSerializer
StringRedisSerializer strSerializer = new StringRedisSerializer();
template.setKeySerializer(strSerializer);
template.setHashKeySerializer(strSerializer);
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);
//value使用Jackson2JsonRedisSerializer
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
}
总结下,当kill掉一个redis集群的主节点(7000节点),从节点7003晋升为主节点,7000节点下线。redis集群故障迁移没问题,但spring boot工程此时要清空旧的redis连接池缓存,重新建立连接池缓存,这样才能把已经无效的7000节点连接池清除掉,不再去连接7000节点。spring boot 2版本要做故障迁移,要注意以下几点。
1、pom.xml导入jedis 2.X版本依赖包。jedis 3.X版本也不行。
2、把application.properties中的配置再次配置到JedisConnectionFactory中,RedisTemplate 使用JedisConnectionFactory作为连接工厂。
当然,也可能是我哪里搞错了,欢迎大家留言讨论。
接下来写一个测试类循环调用redis
@RunWith(SpringRunner.class)
@SpringBootTest
public class Test01 {
@Autowired
RedisTemplate redisTemplate;
@Test
public void test001(){
while (true){
try {
String key = "test:" + new Date().getTime();
redisTemplate.opsForValue().set(key, new Date().getTime());
TimeUnit.MILLISECONDS.sleep(100L);
System.out.println(redisTemplate.opsForValue().get(key));
}catch (Exception e){
e.printStackTrace();
}
}
}
}
运行测试类
打开一个redis-cli
redis-cli -p 7002
查看集群节点配置
cluster nodes
7001是主节点,7005是7001的从节点
kill 主节点7001的进程
ps -ef|grep redis-server
kill -9 master进程号
此时spring boot工程会报异常,经过短暂时间后,工程运行正常。
再次查看redis集群节点
7005晋升为master,7001下线。
再次启动7001节点,7001会变成7005的从节点。
当7001、7005这一对主从节点都下线的时候,由于负责5491-10933槽位端的节点都下线了,get、set到此槽位端的请求都会报错。