Jedis源码分析(三)-JedisCluster的内部实现

组合潮涌
• 阅读 11972

Jedis源码分析共有四个章节,以下为各章链接:

  1. Jedis源码分析(一)-Jedis介绍
  2. Jedis源码分析(二)-Jedis类结构及实现
  3. Jedis源码分析(三)- JedisCluster类结构及实现
  4. Jedis源码分析(四)-JedisSentinel与ShardedJedis介绍

1 JedisCluster的内部结构

首先看一下JedisCluster的类结构,为橘色标志为核心类
Jedis源码分析(三)-JedisCluster的内部实现

图1 JedisCluster的类结构

图1是JedisCluster的类结构,由于Jedis本身不是线程安全的,所以选择使用对象池JedisPool 来保证线程安全。在JedisClusterInfoCache中,除了要保存节点和槽的一一对应关系,还要为每个节点建立一个对象池JedisPool,并保存在map中。因而,这个类主要用于保存集群的配置信息,并且是JedisCluster初始化部分的核心所在。JedisClusterConnectionHandler是cache类的一个窗口,cache类似数据管理层,而Handler就类似于操控数据提供服务的Service层。

2 JedisCluster的初始化

Jedis源码分析(三)-JedisCluster的内部实现

图2-1 JedisCluster初始化调用时序图

Jedis源码分析(三)-JedisCluster的内部实现
图2-2 JedisCluster的初始化详情

​ 图2-1,2-2是JedisCluster初始化的具体实现。Jedis建立集群的过程很清晰,传入节点信息,通过其中一个节点从redis服务器拿到整个集群的信息信息,包括槽位对应关系,主从节点的信息,保存在JedisClusterInfoCache中。

3 JedisCluster的调用流程

使用方法:

Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("127.0.0.1", 7379));
JedisCluster jc = new JedisCluster(jedisClusterNode, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT,
    DEFAULT_REDIRECTIONS, "cluster", DEFAULT_CONFIG);
jc.set("foo", "bar");
assertEquals("bar", jc.get("foo"));

Jedis源码分析(三)-JedisCluster的内部实现

​ 图3-1 JedisCluster 命令时序图

Jedis源码分析(三)-JedisCluster的内部实现
图3-2 JedisCluster 命令调用的具体实现过程

图3-1,3-2是JedisCluster发送请求的具体实现。在发送请求时,JedisCluster对象先从初始化得到的集群map中获取key对应的节点连接,即一个可用的Jedis对象。然后通过这个对象发送get key 命令。

通常,根据key计算槽位得到的节点不会报错。所以如果发生connectionException,或者MovedDataException,说明初始化得到的槽位与节点的对应关系有问题,即与实际的对应关系不符,应当重置map。 如果出现ASK异常,说明数据正在迁移,需要临时使用返回消息指定的地址,重新发送命令。在这里,Jedis通过异常反馈,智能地同步了客户端与服务端的集群信息。

4 Jedis与Redis的版本冲突

​ 在之前的代码分析中,源码均来自于Jedis-2.10,下面我们来看下Jedis-2.7.3cache.discoverClusterNodesAndSlots实现:

//JedisClusterInfoCache类
public void discoverClusterNodesAndSlots(Jedis jedis) {
//清空两个map
    this.nodes.clear();
    this.slots.clear();
    //获取集群信息
    String localNodes = jedis.clusterNodes();
    for (String nodeInfo : localNodes.split("\n")) {
//这个方法处理“cluster nodes”命令的回复消息,先通过” ”分割消息,再通过“:”分割得到“host”和”port”。
        ClusterNodeInformation clusterNodeInfo = 
            nodeInfoParser.parse(nodeInfo, new HostAndPort(
            jedis.getClient().getHost(), jedis.getClient().getPort()));
        HostAndPort targetNode = clusterNodeInfo.getNode();
        setNodeIfNotExist(targetNode);
        assignSlotsToNode(clusterNodeInfo.getAvailableSlots(), targetNode);
      }
}

可以看出与Jedis-2.10不同的是,这里使用CLUSTER NODES获取集群信息。
这是Redis-3.2.9对于CLUSTER NODES命令的回复消息

860abdabe239d096cff13385382325784ad601fc 127.0.0.1:6568 myself,master - 0 0 4 connected 5462-10923
24e8ebefc4d0eaefd6950d30bc389e50aab84286 127.0.0.1:6394 slave 860abdabe239d096cff13385382325784ad601fc 0 1516623794886 4 connected
d27c2ce7de4029b4f2828d67cf45e82211b1369c 127.0.0.1:6569 slave 8f82154da33324208e79bdb9580cea2ca0cada93 0 1516623795905 2 connected
8f82154da33324208e79bdb9580cea2ca0cada93  127.0.0.1:6395 master - 0 1516623793867 2 connected 10924-16383
4e9c6efe120db6463f42512130ed3fc0d1e5d5b8 127.0.0.1:6393 master - 0 1516623792848 1 connected 0-5461
f3fd6e3bb7c4ae9a0c8f33472096109fd7344d4b 127.0.0.1:6567 slave 4e9c6efe120db6463f42512130ed3fc0d1e5d5b8 0 1516623793356 1 connected

这是Redis-4.0.6对于CLUSTER NODES命令的回复消息:

0c32e45ddadca3567ac72f6dd94e034f6daed31e 127.0.0.1:7002@17002 master - 0 1516795121251 2 connected 10923-16383
1ca678b62e6935fc7aa274ac7e91d0e7ea752810 127.0.0.1:7000@17000 myself,master - 0 1516795120000 1 connected 0-5460
adc7904694341b4a6a9e1946d483f92769b99473 127.0.0.1:7001@17001 master - 0 1516795120244 0 connected 5461-10922

可以看出从Redis新版本的回复消息中,仅通过“:”已无法获得端口号,于是就会报异常“7002@17002”无法解析为端口号。而这个问题从Jedis-2.8就被解决了

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
java Redis Jedis存储Java对象
Redis入门–Jedis存储Java对象(Java序列化为byte数组方式)Redis入门–Jedis存储Java对象\(Java序列化为byte数组方式)原文地址:http://alanland.iteye.com/admin/blogs/1600685(https://www.oschina.net/action/GoT
Wesley13 Wesley13
3年前
java使用jeids实现redis2.6的list操作(2)
代码:            https://github.com/viakiba/redis importorg.junit.Test;importredis.clients.jedis.BinaryClient.LIST_POSITION;importredis.clients.jedis.Jedis
Wesley13 Wesley13
3年前
Redis分布式锁
packagecom.sqi;importredis.clients.jedis.Jedis;importredis.clients.jedis.JedisPool;importredis.clients.jedis.params.SetParams;importjava.util.\;importjava.util.concur
Wesley13 Wesley13
3年前
java使用jeids实现redis2.6的key管理(2)
代码:    https://github.com/viakiba/redis主要是:    MIGRATE,MOVE,OBJECT,PERSIST/PEXPIRE/PEXPIREAT,PTTL/TTLimportorg.junit.Test;importredis.clients.jedis.Jedis;
Stella981 Stella981
3年前
Redis Cluster集群主从方案
RedisCluster集群主从方案本文介绍一种通过Jedis和Cluster实现Redis集群(主从)的高可用方案,该方案需要使用Jedis2.8.0(推荐),Redis3.0及以上版本(强制).附:RedisCluster集群主从方案:http://www.cnblogs.com/soul
Stella981 Stella981
3年前
Redis应用学习(三)——Jedis客户端的简单使用介绍
1\.Redis的Java客户端程序——Jedis  1.在Redis的安装目录中rediscli是一个Redis的Shell语言写的客户端,通过运行该客户端就可以实现一系列Redis操作,而如果要使用Java语言的Redis客户端,就必须要使用Jedis相关的包。首先导入Jedis相关的包
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
3年前
Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式)介绍
在这里对jedis关于事务、管道和分布式的调用方式做一个简单的介绍和对比:一、普通同步方式最简单和基础的调用方式,@Testpublic void test1Normal() {    Jedis jedis  new Jedis("localhost");    long start  Syst
Stella981 Stella981
3年前
Redis的Util工具类,超好用!
packagecom.lgdz.zhenshiUtil;importredis.clients.jedis.Jedis;importredis.clients.jedis.JedisPool;importredis.clients.jedis.JedisPoolConfig;importjava.