Redis第2️⃣2️⃣课 Cluster客户端路由
2019-05-30 本文已影响2人
小超_8b2f
一、moved重定向
1.图解



2.代码验证
#集群模式连接redis-cli
$ redis-cli -c -p 7000
127.0.0.1:7000> keys *
(empty list or set)
127.0.0.1:7000> set hello world
OK
127.0.0.1:7000> get hello
"world"
#被重定向到9244槽所在的节点上去了
127.0.0.1:7000> set php 'is the best'
-> Redirected to slot [9244] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get php # 能够获取到
"is the best"
127.0.0.1:7001> CLUSTER KEYSLOT php #计算php键的slot值
(integer) 9244
127.0.0.1:7001> exit
#以单机模式连接客户端并查询php节点,报moved异常
$ redis-cli -p 7000
127.0.0.1:7000> get php
(error) MOVED 9244 127.0.0.1:7001
二、ask重定向


moved和ask的区别
- 两者都是客户单重定向
- moved:槽已确认转移
- ask:槽还在转移过程中
三、smart客户端
1. smart客户端原理:追求性能
未采用代理模式,虽操作方便,但是会损耗性能。作者在设计的时候考虑的是直连。
1)从集群中选一个可运行的节点,使用cluster slots 初始化槽和节点映射。
2)将Cluster slots的结果映射到本地,为每个节点创建JedisPool
3)准备执行命令

2. smart客户端使用:JedisCluster
1)JedisCluster基本使用
Set<HostAndPort> nodeList = new HashSet<>();
nodeList.add(new HostAndPort(HOST1,PORT1));
nodeList.add(new HostAndPort(HOST2,PORT2));
nodeList.add(new HostAndPort(HOST3,PORT3));
nodeList.add(new HostAndPort(HOST4,PORT4));
nodeList.add(new HostAndPort(HOST5,PORT5));
nodeList.add(new HostAndPort(HOST6,PORT6));
JedisCluster redisCluster = new JedisCluster(nodeList,timeout,poolConfig);
redisCluster.commond......
2)使用技巧
(1)单例:内置初始化了所有节点的连接池(包括从节点),保证在做故障转移的时候也有客户端连接池。(设置成spring的bean)
(2)无需手动借还连接池
(3)合理配置commons-pool配置
3)JedisCluster实例小程序
public class JedisClusterFactory {
private static Logger logger = LoggerFactory.getLogger(JedisClusterFactory.class);
public static void main(String[] args){
Set<HostAndPort> node = new HashSet<>();
String host = "127.0.0.1";
int port = 7000;
for (int i = 0; i < 7; i++)
node.add(new HostAndPort(host,port++));
JedisPoolConfig poolConfig = new JedisPoolConfig();//设定配置.....
JedisCluster redisCluster = null;
try {
redisCluster = new JedisCluster(node, 5000, poolConfig);
Set<String> keys = redisCluster.keys("{*}");
keys.forEach((x) -> System.out.println(x));
String set = redisCluster.set("hello", "world2");
System.out.println(set);
System.out.println(redisCluster.get("hello"));
System.out.println(redisCluster.get("php"));
clusterNodes(redisCluster); //多节点分别执行命令
} catch (Exception e) {
logger.debug(e.getMessage(), e);
} finally {
redisCluster.close();
}
}
4)多节点命令实现
/**
* 多节点分别执行命令实现
* eg:scan命令是扫描所有节点的键值,
* 对于Redis Cluster来说不支持一个scan命令扫描所有节点
* 需求,在所有节点上去执行这个命令
*/
public static void clusterNodes(JedisCluster redisCluster) {
//获取所有节点的JedisPool
Map<String, JedisPool> clusterNodes = redisCluster.getClusterNodes();
Set<Entry<String, JedisPool>> entrySet = clusterNodes.entrySet();
for(Entry<String, JedisPool> entry : entrySet) {
JedisPool value = entry.getValue();
Jedis jedis = value.getResource();
Set<String> keys = jedis.keys("*");
System.out.println((isMaster(jedis) ? "master" : "slave") + " | " + entry.getKey() + " | " + keys);
if(!isMaster(jedis)) { //只对主节点数据进行修改
continue;
}
}
}
/**
* 判断是否是主节点
*/
public static boolean isMaster(Jedis jedis) {
String infos = jedis.info("Replication");
String[] split = infos.split("\n");
for(String info: split)
if(info.startsWith("role"))
return info.contains("master");
return false;
}
}
OK
world2
is the best
slave | 127.0.0.1:7004 | [php]
slave | 127.0.0.1:7005 | []
master | 127.0.0.1:7002 | []
slave | 127.0.0.1:7003 | [hello]
master | 127.0.0.1:7000 | [hello]
master | 127.0.0.1:7001 | [php]
四、 如何实现批量命令操作?
mget、mset、hmget、hmset ......
1. mset、mget的键必须在一个槽,否则报错
//无论hello,hell是否在同一个节点上都会报这个❌,和槽有关,和节点无关
List<String> mget = redisCluster.mget("hello","hell");
System.out.println(mget);
16:48:18.511 [main] DEBUG com.designPattern.jedis.JedisClusterFactory - No way to dispatch this command to Redis Cluster because keys have different slots.
redis.clients.jedis.exceptions.JedisClusterOperationException: No way to dispatch this command to Redis Cluster because keys have different slots.
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:39)
at redis.clients.jedis.JedisCluster.mget(JedisCluster.java:1459)
at com.designPattern.jedis.JedisClusterFactory.main(JedisClusterFactory.java:42)
思考:一个槽可以放多少数据?可以放多少个key?
2. 四种批量优化的方法
1) 串行mget
通过for循环redisCluster.getClusterNodes();获取每个Jedis,然后分别mget
简单、效率差
串行mget
2) 串行IO
在本地进行了一个内聚,前提:知道所有的key。通过本地的CRC16(key)取余,算出槽,通过槽和节点的对应关系对key进行分组
串行IO图解
3) 并行IO

4) hash_tag

