jedis 集群模式下连接redis原理
2016-10-12 18:19
411 查看
1.ShardedJedis内部实现
首先我们来看一下ShardedJedis的一个继承关系看完了图,那么我们一步一步跟着我们的代码调用来看,以我们最简单的 ShardedJedis.get(key)方法为例:
public String get(String key) { Jedis j = getShard(key); return j.get(key); }
这边有调用一个getShard 方法,参数为我们传入的key,然后返回一个普通的jedis对象,那么这个getShard是用来做什么的呢,大家可能已经猜到了,这个方法就是会根据我们传入的key做一致性哈希判断,然后返回key落到的那个redis实例上的一个redis连接,不同的key返回的redis连接可能是不同的。
进入getShard 方法,你会发现这个实现是在Sharded类中实现的(看上面的类图可以发现顶层的Sharded类),代码如下:
public R getShard(String key) { return resources.get(getShardInfo(key)); } public S getShardInfo(byte[] key) { SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key)); if (tail.isEmpty()) { return nodes.get(nodes.firstKey()); } return tail.get(tail.firstKey()); } public S getShardInfo(String key) { return getShardInfo(SafeEncoder.encode(getKeyTag(key))); }
上面的方法是层层调用的关系,在这边不细说,我们主要看下第二个方法(getShardInfo(byte[] key))实现(上面的nodes变量是一个TreeMap 类型, algo 是Hashing类型,即key分片所使用的hash算法,这个在前一篇有简单说过),那么这段代码的含义我们大概成猜出来了,
就是在一个TreeMap中取出大于等于key之后的部分视图SortMap
在SortMap取得第一个键值对的值,然后返回一个 S 对象,
然后根据这个S 对象,去resources(resources
= new LinkedHashMap<ShardInfo<R>, R>())中get一个R对象
那么这个S、 R对象各自代表什么呢?看下面的代码
public class Sharded<R, S extends ShardInfo<R>>
public class BinaryShardedJedis extends Sharded<Jedis, JedisShardInfo> implements BinaryJedisCommands
可以得出 S = JedisShardInfo, R = Jedis 对象,即在TreeMap存储了服务器划分的虚拟节点的信息,LinkedHashMap中存储了服务器的物理连接。
JedisShardInfo具体信息如下:里面包含了jedis服务器的一些信息,最重要的是它的父类中有一个weight字段,作为本jedis服务器的权值。
ok,那我们了解了实际上就是根据jedis服务器的信息去获取一个jedis的连接,返回给上层调用。
我们可以梳理下这个逻辑:
当我们使用ShardedJedis去查一个key时,首先它会把这个key进行一个hash算法
根据这个hash值然后去treeMap中,查出这个key落在哪个实例中,并返回redis实例对应的具体信息
根据这个redis的实例信息,到一个保存jedis链接和实例信息对应关系的LinkedHashMap中找到这个jedis连接
最终返回jedis连接,执行对象的命令操作(到这步后实际上和单机操作一样了)
那么我们的nodes 服务器虚拟节点和resources 服务器物理连接是什么时候初始化的呢,接下来继续看
2.redis虚拟节点(nodes)和物理连接(resources) 初始化
我们继续看Sharded的构造方法public Sharded(List<S> shards, Hashing algo) { this.algo = algo; initialize(shards); } public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) { this.algo = algo; this.tagPattern = tagPattern; initialize(shards); }
这边有一个initialize方法,就是用来对虚拟节点和物理连接进行初始化的,看其实现
private void initialize(List<S> shards) { nodes = new TreeMap<Long, S>(); for (int i = 0; i != shards.size(); ++i) { final S shardInfo = shards.get(i); if (shardInfo.getName() == null) for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo); } else for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo); } resources.put(shardInfo, shardInfo.createResource()); } }
具体细节就不说了,根据上面的,我们就知道是在这边进行了初始化,将每台服务器节点采用hash算法划分为160个虚拟节点(可以配置划分权重),保存在TreeMap中,
然后把每台服务器节点的信息和物理连接以键值对保存LinkedHashMap中。
然后通过一系列的查找,发现Sharded的构造方法其实是在我们 jedisPool.getResource() 时就完成的
//初始化ShardedJedisPool List<JedisShardInfo> infoList = Arrays.asList(shardInfo1, shardInfo2, shardInfo3); ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, infoList); ShardedJedis jedis = jedisPool.getResource();
纳尼? 每次jedisPool.getResource() 才初始化?那会不会造成很慢呢,其实不用担心,其底层是使用了commons.pool来进行连接池的一些操作,会根据我们配置的连接池参数来生成对应的连接并保存,其中的细节很复杂,博主没有继续深究,实现其实和数据库连接池是一致的。
3.总结
ShardedJedis分布式具体的的实现思路:
redis服务器节点划分:将每台服务器节点采用hash算法划分为160个虚拟节点(可以配置划分权重)
将划分虚拟节点采用TreeMap存储
对每个redis服务器的物理连接采用LinkedHashMap存储
对Key or KeyTag 采用同样的hash算法,然后从TreeMap获取大于等于键hash值得节点,取最邻近节点存储;当key的hash值大于虚拟节点hash值得最大值时,存入第一个虚拟节点
sharded采用的hash算法:MD5 和 MurmurHash两种;默认采用64位的MurmurHash算法;
相关文章推荐
- jedis针对三种redis工作模式的连接方式
- redis3.0.0 集群安装详细步骤及jedis连接redis集群代码实现
- redis集群--4.jedis连接集群版
- redis集群客户端JedisCluster优化 - 管道(pipeline)模式支持
- 使用Jedis连接集群Redis(支持Redis复杂操作)
- Centos 安装部署redis集群 及 jedis连接集群遇到的问题
- Spring集成Jedis(不依赖spring-data-redis)(单机/集群模式)(待实践)
- 使用jedis连接单机和集群redis的两种方式
- redis客户端jedis基于spring搭建单节点或者集群线程池连接
- Redis 一二事 - 在spring中使用jedis 连接调试单机redis以及集群redis
- Jedis连接redis集群 java.lang.NumberFormatException: For input string: "7006@17006"
- Redis 一二事 - 在spring中使用jedis 连接调试单机redis以及集群redis
- redis--jedis连接集群
- redis集群客户端JedisCluster优化 - 管道(pipeline)模式支持
- 使用Jedis连接单机版redis和集群般redis
- redis4.0 集群,jedis客户端连接配置
- 【Redis】搭建集群与如何使用Jedis连接集群
- 使用jedis连接单机和集群redis的两种方式
- 用Jedis获取redis连接(集群和非集群状态下)
- 【Redis缓存机制】14.Java连接Redis_Jedis_主从模式