首页 > 学院 > 开发设计 > 正文

Jedis下的ShardedJedis(分布式)使用方法(二)

2019-11-14 14:51:55
字体:
来源:转载
供稿:网友

上一篇中介绍了ShardedJedis的基本使用方法以及演示了一个简单的例子,在这一篇中我们来介绍了ShardedJedis的原理。

 

1.ShardedJedis内部实现

首先我们来看一下ShardedJedis的一个继承关系

 

看完了图,那么我们一步一步跟着我们的代码调用来看,以我们最简单的%20ShardedJedis.get(key)方法为例:

%20%20public%20String%20get(String%20key)%20{%20%20%20%20Jedis%20j%20=%20getShard(key);%20%20%20%20return%20j.get(key);%20%20}

 

这边有调用一个getShard%20方法,参数为我们传入的key,然后返回一个普通的jedis对象,那么这个getShard是用来做什么的呢,大家可能已经猜到了,这个方法就是会根据我们传入的key做一致性哈希判断,然后返回key落到的那个redis实例上的一个redis连接,不同的key返回的redis连接可能是不同的。

 

进入getShard%20方法,你会发现这个实现是在Sharded类中实现的(看上面的类图可以发现顶层的Sharded类),代码如下:

public%20R%20getShard(String%20key)%20{%20%20%20%20return%20resources.get(getShardInfo(key));%20%20}%20%20public%20S%20getShardInfo(byte[]%20key)%20{%20%20%20%20SortedMap<Long,%20S>%20tail%20=%20nodes.tailMap(algo.hash(key));%20%20%20%20if%20(tail.isEmpty())%20{%20%20%20%20%20%20return%20nodes.get(nodes.firstKey());%20%20%20%20}%20%20%20%20return%20tail.get(tail.firstKey());%20%20}%20%20public%20S%20getShardInfo(String%20key)%20{%20%20%20%20return%20getShardInfo(SafeEncoder.encode(getKeyTag(key)));%20%20}

 

上面的方法是层层调用的关系,在这边不细说,我们主要看下第二个方法(getShardInfo(byte[]%20key))实现(上面的nodes变量是一个TreeMap%20类型,%20algo%20Hashing类型,即key分片所使用的hash算法,这个在前一篇有简单说过),那么这段代码的含义我们大概成猜出来了,

  1. 就是在一个TreeMap中取出大于等于key之后的部分视图SortMap
  2. 在SortMap取得第一个键值对的值,然后返回一个%20S%20对象,
  3. 然后根据这个S%20对象,去resources(resources%20=%20new%20LinkedHashMap<ShardInfo<R>,%20R>())中get一个R对象

 

那么这个S%20R对象各自代表什么呢?看下面的代码

public%20class%20Sharded<R,%20S%20extends%20ShardInfo<R>>
public%20class%20BinaryShardedJedis%20extends%20Sharded<Jedis,%20JedisShardInfo>%20implements%20%20%20%20BinaryJedisCommands

可以得出 %20S%20=%20JedisShardInfo,%20R%20=%20Jedis%20对象,即在TreeMap存储了服务器划分的虚拟节点的信息,LinkedHashMap中存储了服务器的物理连接。 

 

JedisShardInfo具体信息如下:里面包含了jedis服务器的一些信息,最重要的是它的父类中有一个weight字段,作为本jedis服务器的权值。

 

ok,那我们了解了实际上就是根据jedis服务器的信息去获取一个jedis的连接,返回给上层调用。

 

我们可以梳理下这个逻辑:

  1. 当我们使用ShardedJedis去查一个key时,首先它会把这个key进行一个hash算法
  2. 根据这个hash值然后去treeMap中,查出这个key落在哪个实例中,并返回redis实例对应的具体信息
  3. 根据这个redis的实例信息,到一个保存jedis链接和实例信息对应关系的LinkedHashMap中找到这个jedis连接
  4. 最终返回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分布式具体的的实现思路:

 好了,大概的实现如上面所说的,都是图可能会有点乱,大家如果有什么问题或者发现了什么错误,please tell me!

 


上一篇:tomcat启动错误——Achildcontainerfailedduringstart(问题已解决)

下一篇:Eclipse中java项目转web项目的方法

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
学习交流
热门图片
猜你喜欢的新闻
猜你喜欢的关注

新闻热点

疑难解答

图片精选

网友关注