Redis学习--JedisCluster源码解读
来源:互联网 发布:网络大电影编剧收费 编辑:程序博客网 时间:2024/06/06 00:09
JedisCluster
- JedisCluster是针对RedisCluster的JAVA客户端,它封装了java访问redis集群的各种操作,包括初始化连接,请求重定向等操作。具体内部实现原理主要有如下两个方面:
1.1. JedisCluster初始化时,所有的集群连接信息都是封装在JedisClusterInfoCache这类中。
1.2. JedisClusterInfoCache类中有两个非常重要的Map数据结构,分别是
/** **nodes存放集群IP地址信息和JedisPool。其中String是IP:Port信息,集群中有多少个节点,IP:Port就有多少个。 **/ Map<String, JedisPool> nodes = new HashMap<String, JedisPool>(); /** **slots中存储key-value是集群中数据槽的编号和jedisPool实例。即slots中有16384个键值对。 **/ Map<Integer, JedisPool> slots = new HashMap<Integer, JedisPool>();
JedisCluster类图
1. 从图中可以看出,JedisCluster主要是继承二进制的BinaryJedisCluster类,这个类中的各种操作都是基于字节数组方式进行的。而且BinaryJedisCluster类实现的4个接口中有3个是基于字节数组操作。
2. JedisCluster实现了JedisCommands,MultiKeyJedisClusterCommands,JedisClusterScriptingCommands接口。这三个接口提供的基于字符串类型的操作,即key都是字符串类型。
3. BasicCommands是关于redis服务本身基本操作,比如save,ping,bgsave等操作。
4. MultiKeyBinaryJedisClusterCommands和MultiKeyJedisClusterCommands接口一个字节数组的批量操作,一个是字符串的批量操作。
JedisClusterCommand
- 在JedisCluster客户端中,JedisClusterCommand是一个非常重要的类,采用模板方法设计,高度封装了操作Redis集群的操作。对于集群中各种存储操作,提供了一个抽象execute方法。
- JedisCluster各种具体操作Redis集群方法,只需要通过匿名内部类的方式,灵活扩展Execute方法。
- 内部通过JedisClusterConnectionHandler封装了Jedis的实例。
- JedisClusterCommand源码分析:
/****该类主要由两个重点:采用模板方法设计,具体存取操作有子类实现**在集群操作中,为了保证高可用方式,采用递归算法进行尝试发生的MOVED,ASK,数据迁移操作等。**/public abstract class JedisClusterCommand<T> { // JedisCluster的连接真正持有类 private JedisClusterConnectionHandler connectionHandler; // 尝试次数,默认为5 private int maxAttempts; private ThreadLocal<Jedis> askConnection = new ThreadLocal<Jedis>(); public JedisClusterCommand(JedisClusterConnectionHandler connectionHandler, int maxAttempts) { this.connectionHandler = connectionHandler; this.maxAttempts = maxAttempts; } // redis各种操作的抽象方法,JedisCluster中都是匿名内部类实现。 public abstract T execute(Jedis connection); // public T run(String key) { if (key == null) { throw new JedisClusterException("No way to dispatch this command to Redis Cluster."); } return runWithRetries(SafeEncoder.encode(key), this.maxAttempts, false, false); } /*** *** 该方法采用递归方式,保证在往集群中存取数据时,发生MOVED,ASKing,数据迁移过程中遇到问题,也是一种实现高可用的方式。 ***该方法中调用execute方法,该方法由子类具体实现。 ***/ private T runWithRetries(byte[] key, int attempts, boolean tryRandomNode, boolean asking) { if (attempts <= 0) { throw new JedisClusterMaxRedirectionsException("Too many Cluster redirections?"); } Jedis connection = null; try { /** *第一执行该方法,asking为false。只有发生JedisAskDataException *异常时,才asking才设置为true **/ if (asking) { connection = askConnection.get(); connection.asking(); // if asking success, reset asking flag asking = false; } else { // 第一次执行时,tryRandomNode为false。 if (tryRandomNode) { connection = connectionHandler.getConnection(); } else { /** 根据key获取分配的嘈数,然后根据数据槽从JedisClusterInfoCache 中获取Jedis的实例 **/ connection = connectionHandler.getConnectionFromSlot(JedisClusterCRC16.getSlot(key)); } } /*** ** 调用子类方法的具体实现。 **/ return execute(connection); } catch (JedisNoReachableClusterNodeException jnrcne) { throw jnrcne; } catch (JedisConnectionException jce) { //释放已有的连接 releaseConnection(connection); connection = null; /*** ***只是重建键值对slot-jedis缓存即可。已经没有剩余的redirection了。 ***已经达到最大的MaxRedirection次数,抛出异常即可。 ***/ if (attempts <= 1) { this.connectionHandler.renewSlotCache(); throw jce; } // 递归调用该方法 return runWithRetries(key, attempts - 1, tryRandomNode, asking); } catch (JedisRedirectionException jre) { // if MOVED redirection occurred, if (jre instanceof JedisMovedDataException) { // 发生MovedException,需要重建键值对slot-Jedis的缓存。 this.connectionHandler.renewSlotCache(connection); } // release current connection before recursion or renewing releaseConnection(connection); connection = null; if (jre instanceof JedisAskDataException) { asking = true; // 该异常说明数据还在当前数据槽中,只是再查询一次即可。 askConnection.set(this.connectionHandler.getConnectionFromNode(jre.getTargetNode())); } else if (jre instanceof JedisMovedDataException) { } else { throw new JedisClusterException(jre); } // 递归调用。 return runWithRetries(key, attempts - 1, false, asking); } finally { releaseConnection(connection); } } private void releaseConnection(Jedis connection) { if (connection != null) { connection.close(); } }}
JedisClusterInfoCache
- 这个缓存类主要完成如下功能:
1.1. 缓存键值对IP:Port——>JedisPool,缓存键值对slot——>JedisPool。 - 将redisCluster中的每一个数据槽对应的jedis实例事先加入缓存,每个数据节点的连接信息缓存到本地中。
- 该类中最重要的方法就是discoverClusterNodesAndSlots(Jedis),源码如下:
public void discoverClusterNodesAndSlots(Jedis jedis) { w.lock(); try { reset(); /**根据当前redis实例,获取集群中master,slave节点信息。包括每个master节点 上分配的数据嘈。slots结果如下: ****[10923, 16383, [[B@924fda2, 9000], [[B@5b879b5e, 9001]]] *** [[5461, 10922, [[B@3681fe9a, 7001], [[B@10724c6b, 8000]], *** [0, 5460, [[B@3ff70d3c, 7000], [[B@7485fef2, 8001]], ***/ List<Object> slots = jedis.clusterSlots(); // 遍历List中的集合,总共就3个master节点信息 for (Object slotInfoObj : slots) { // slotInfo指一个master,slave,分配槽的个数信息 List<Object> slotInfo = (List<Object>) slotInfoObj; if (slotInfo.size() <= MASTER_NODE_INDEX) { continue; } // 获取分配到每一个master节点的数据槽的个数 List<Integer> slotNums = getAssignedSlotArray(slotInfo); /**slotInfo的大小为4,其中前两项为槽数的最小值和最大值。 *** 后两项为master,slave实例信息 ***[10923, 16383, [[B@924fda2, 9000], [[B@5b879b5e, 9001]]] ***/ int size = slotInfo.size(); for (int i = MASTER_NODE_INDEX; i < size; i++) { // hostInfos就是[B@924fda2, 9000]集合。就俩元素 List<Object> hostInfos = (List<Object>) slotInfo.get(i); if (hostInfos.size() <= 0) { continue; } //根据ip,port构建HostAndPort实例 HostAndPort targetNode = generateHostAndPort(hostInfos); /**根据HostAndPort解析出ip:port的key值, **再根据key从缓存中查询对应的jedisPool实例。如果没有jedisPool实例, **就创建JedisPool实例,最后放入缓存中。 ** key的值是 ip:port,value的值是jedisPool **/ setupNodeIfNotExist(targetNode); if (i == MASTER_NODE_INDEX) { // 将每一个数据槽对应的jedisPool缓存起来。 // key的值是:数据槽的下标,value的值是 JedisPool。 // slots缓存中总共有16384的key-value键值对 assignSlotsToNode(slotNums, targetNode); } } } } finally { w.unlock(); } }
阅读全文
0 0
- Redis学习--JedisCluster源码解读
- 分布式缓存技术redis学习系列(八)——JedisCluster源码解读:集群初始化、slot(槽)的分配、值的存取
- redis学习笔记(二)JedisCluster + redis 3.2.5集群
- Redis JedisCluster Spring整合
- redis - JedisCluster Spring整合
- redis集群JedisCluster优化
- JedisCluster 操作集群Redis
- Redis学习笔记(三)Redis源码解读
- Redis学习笔记(三)Redis源码解读
- JedisCluster源码解读:集群初始化、slot(槽)的分配、值的存取
- Redis学习笔记(五)jedis(JedisCluster)操作Redis集群 redis-cluster
- redis集群客户端JedisCluster优化
- 使用jedisCluster操作Redis集群
- Redis String 源码解读1
- 分布式缓存技术redis学习系列(七)——spring整合jediscluster
- jedisCluster
- pimple学习:源码解读
- Redis的Java客户端源码解读
- DRN和WRN
- python 自带 web服务器-保存为start_server.bat
- 微机接口实验一 :8255并行接口实验
- 成为Java GC专家(3)—如何优化Java垃圾回收机制
- 第十一周 项目5
- Redis学习--JedisCluster源码解读
- Andrew Ng's deeplearning Course3Week1 ML Strategy(结构化机器学习)
- MyBatis
- SpringBoot如何添加拦截器
- 关于递推算法的研究
- a newbie in Porto Seguro’s Safe Driver Prediction(solo参赛 TOP 5%)
- c++之数组与字符串
- react-native总结之项目开发环境创建
- 利用 js-xlsx 实现 Excel 文件导入并解析Excel数据成json格式的数据并且获取其中某列数据