Redis5.x集群学习须知
学前须知:
当前redis的最新版本是5.0以上,其搭建cluster的方法与早期的redis4.0以前的不太一样,不再使用ruby相关的组件。
-
1.redis集群的常见搭建方式:划分大概是5种方式
-
2.Redis集群的原理和机制
-
3.学会搭建一个Redis集群
-
4.测试搭建好的集群:连接测试,高可用性测试
已经建设好的集群类似如下方式:采用对key做hash后,分到不同区段的slot的方式,同时master节点宕机,slave节点切换顶替。实现redis高负载和高可用。
3.1 使用java代码连接RedisCluster集群实现
package redistest;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
/** 使用JedisCluster类中各种构造函数,可以连接Jedis Cluster(Client Sharding)集群
* @author fangchangtan
* @date 2019-10-15 19:55
*/
public class RedisClusterTest {
public static void main(String[] args) throws IOException {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
// 最大连接数
config.setMaxTotal(30);
// 最大空闲数
config.setMaxIdle(10);
// 最大允许等待时间,如果超过这个时间还未获取到连接,则会报JedisException异常:
// Could not get a resource from the pool
config.setMaxWaitMillis(2000);
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("172.19.32.xxx", 7001));
jedisClusterNode.add(new HostAndPort("172.19.32.xxx", 7002));
jedisClusterNode.add(new HostAndPort("172.19.32.xxx", 7003));
jedisClusterNode.add(new HostAndPort("172.19.32.yyy", 7004));
jedisClusterNode.add(new HostAndPort("172.19.32.yyy", 7005));
jedisClusterNode.add(new HostAndPort("172.19.32.yyy", 7006));
//1.如果集群没有密码
// JedisCluster jc = new JedisCluster(jedisClusterNode,config);
//2.如果使用到密码,请使用如下构造函数
JedisCluster jc = new JedisCluster(jedisClusterNode, 1000,30,3,"root",config);
jc.set("foo", "bar");
jc.set("test", "value");
jc.set("52", "poolTestValue288");
jc.set("44", "444");
jc.set("name", "poolTestValue2");
System.out.println("==================");
System.out.println( jc.decr("44"));
System.out.println(jc.get("name"));
System.out.println(jc.get("52"));
System.out.println(jc.get("test"));
jc.close();
}
}
运行结果:
3.2 常见问题总结:
问题1 java连接Redis异常:JedisMovedDataException
redis.clients.jedis.exceptions.JedisMovedDataException: MOVED 1539 127.0.0.1:6379
解决方案: 将连接对象从 Jedis 换成 JedisCluster。就可以了。
问题原因 : MOVED表示使用的是Redis群集。而 Jedis 不是集群模式。
//import redis.clients.jedis.HostAndPort;
//import redis.clients.jedis.JedisCluster;
HostAndPort hostAndPort = new HostAndPort(host, port);
Set<HostAndPort> hostAndPortSet = new HashSet<>();
hostAndPortSet.add(hostAndPort);
JedisCluster jedis = new JedisCluster(hostAndPortSet);
jedis.setnx(key, value);
问题2:NOAUTH Authentication required.
Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required.
解决方案: 可以选择相应的JedisCluster类的构造函数
其中password字段即为要填写的集群登录密码
public JedisCluster(Set<HostAndPort> jedisClusterNode,int connectionTimeout, int soTimeout,int maxAttempts, String password, final GenericObjectPoolConfig poolConfig) {
super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, poolConfig);
}
问题原因 : 如果Redis配置了密码,则java连接redisCluster时候需要添加密码配置连接。
问题3 :JedisClusterMaxAttemptsException: No more cluster attempts left.
测试redis cluster集群高可用性的时候,当杀死master节点之后,java连接rediscluster的代码出现如下错误!
redis.clients.jedis.exceptions.JedisClusterMaxAttemptsException: No more cluster attempts left.
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:86)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:124)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:124)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:124)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:124)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:124)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:25)
at redis.clients.jedis.JedisCluster.set(JedisCluster.java:143)
at redistest.RedisClusterTest.main(RedisClusterTest.java:48)
这个问题碰到了很长是哪没有解决,没办法,只能采用折中的方法。
解决方案: 在java代码中捕捉slave切换到master过程中异常错误
JedisCluster jc = new JedisCluster(nodes, 15000,3, config);
for (int i = 0; i < 1000; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(jc.toString());
jc.set("foo", "bar");
jc.set("test", "value");
jc.set("52", "poolTestValue288");
jc.set("44", "444");
jc.set("name", "poolTestValue2");
jc.set(""+i,""+i);
System.out.println("==================");
// System.out.println( jc.decr("44"));
System.out.println(jc.get("name"));
System.out.println(jc.get("52"));
System.out.println(jc.get("test"));
System.out.println(jc.get(""+i));
} catch (JedisClusterMaxAttemptsException e) {
jc.close();
//当slave切换到master过程中,cluster捕捉到该异常时候
jc = new JedisCluster(nodes, 15000,5, config);
e.printStackTrace();
}
}
}
jc.close();
这种只能暂时的解决一部分问题,但是会有数据相应的丢失!小伙伴们有什么好的办法,欢迎提出来,多谢啦!
问题原因:master挂了之后,slave升级为master节点之后,对于java客户端来说无感(没有感觉到元信息的变化),还是在尝试连接原来master的slot,导致失。