专栏首页JavaEdgeSharded源码分析1. 概述2. 继承关系3. 构造函数4. 哈希环的初始化数据定位

Sharded源码分析1. 概述2. 继承关系3. 构造函数4. 哈希环的初始化数据定位

1. 概述

当业务的数据量非常庞大时,需要考虑将数据存储到多个缓存节点上,如何定位数据应该存储的节点,一般用的是一致性哈希算法。 Jedis在客户端角度实现了一致性哈希算法,对数据进行分片,存储到对应的不同的redis实例中。 Jedis对Sharded的实现主要是在ShardedJedis.javaShardedJedisPool.java中。 本文主要介绍ShardedJedis的实现,ShardedJedisPool是基于apache的common-pool2的对象池实现。

2. 继承关系

3. 构造函数

参数

  • shards 一个JedisShardInfo的列表,一个JedisShardedInfo类代表一个数据分片的主体
  • algo 数据分片算法
  • keyTagPattern 自定义分片算法所依据的key的形式。例如,可以不针对整个key的字符串做哈希计算,而是类似对thisisa{key}中包含在大括号内的字符串进行哈希计算

JedisShardInfo源码

public class JedisShardInfo extends ShardInfo<Jedis> {
    public String toString() {
    return host + ":" + port + "*" + getWeight();
    }

    private int timeout;
    private String host;
    private int port;
    private String password = null;
    private String name = null;

    public String getHost() {
    return host;
    }

    public int getPort() {
    return port;
    }

    public JedisShardInfo(String host) {
    super(Sharded.DEFAULT_WEIGHT);
    URI uri = URI.create(host);
    if (uri.getScheme() != null && uri.getScheme().equals("redis")) {
        this.host = uri.getHost();
        this.port = uri.getPort();
        this.password = uri.getUserInfo().split(":", 2)[1];
    } else {
        this.host = host;
        this.port = Protocol.DEFAULT_PORT;
    }
    }

    public JedisShardInfo(String host, String name) {
    this(host, Protocol.DEFAULT_PORT, name);
    }

    public JedisShardInfo(String host, int port) {
    this(host, port, 2000);
    }

    public JedisShardInfo(String host, int port, String name) {
    this(host, port, 2000, name);
    }

    public JedisShardInfo(String host, int port, int timeout) {
    this(host, port, timeout, Sharded.DEFAULT_WEIGHT);
    }

    public JedisShardInfo(String host, int port, int timeout, String name) {
    this(host, port, timeout, Sharded.DEFAULT_WEIGHT);
    this.name = name;
    }

    public JedisShardInfo(String host, int port, int timeout, int weight) {
    super(weight);
    this.host = host;
    this.port = port;
    this.timeout = timeout;
    }

    public JedisShardInfo(URI uri) {
    super(Sharded.DEFAULT_WEIGHT);
    this.host = uri.getHost();
    this.port = uri.getPort();
    this.password = uri.getUserInfo().split(":", 2)[1];
    }

    @Override
    public Jedis createResource() {
    return new Jedis(this);
    }
}

可见JedisShardInfo包含了一个redis节点主机号,端口号,名称,密码等相关信息 要构造一个ShardedJedis,需提供一个或多个JedisShardInfo

最终构造函数的实现在其父类Sharded

4. 哈希环的初始化

Sharded类维护了

  • TreeMap 基于红黑树实现,用来存放经过一致性哈希计算后的redis节点,
  • LinkedHashMap 用来保存ShardInfo与Jedis实例的对应关系。

定位的流程如下 先在TreeMap中找到对应key所对应的ShardInfo,然后通过ShardInfo在LinkedHashMap中找到对应的Jedis实例

Sharded类对这些实例变量的定义如下所示:

    public static final int DEFAULT_WEIGHT = 1;
    private TreeMap<Long, S> nodes;
    private final Hashing algo;
    private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>();

    // 用于提取密钥标签的默认模式。 该模式必须有一个组(在圆括号之间),它将标签分隔开来进行散列。 空模式可以避免为每个查找应用正则表达式,因此不会使用关键标记来改进性能。
    private Pattern tagPattern = null;

    public static final Pattern DEFAULT_KEY_TAG_PATTERN = Pattern
        .compile("\\{(.+?)\\}");

接下来看其构造函数中的initialize方法

可以看到,它对每一个ShardInfo通过一定规则计算其哈希值,然后存到TreeMap中,这里它实现了一致性哈希算法中虚拟节点的概念,因为我们可以看到同一个ShardInfo不止一次被放到TreeMap中,数量是,权重*160。

增加了虚拟节点的一致性哈希有很多好处,比如能避免数据在redis节点间分布不均匀

然后,在LinkedHashMap中放入ShardInfo以及其对应的Jedis实例,通过调用其自身的createSource()来得到jedis实例

数据定位

从ShardedJedis的代码中可以看到,无论进行什么操作,都要先根据key来找到对应的Redis,然后返回一个可供操作的Jedis实例。 例如其set方法

而getShard方法则在Sharded.java中实现,其源代码如下所示:

可以看到,先通过getShardInfo方法从TreeMap中获得对应的ShardInfo,然后根据这个ShardInfo就能够再LinkedHashMap中获得对应的Jedis实例了

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计模式实战-迭代器模式

    迭代器是为容器服务的,那什么是容器呢? 能容纳对象的所有类型都可以称之为容器,例如Collection集合类型、Set类型等,迭代器模式就是为解决遍历这些容器中...

    JavaEdge
  • Java8编程思想(十二)-容器持有对象(下)

    堆栈是“后进先出”(LIFO)集合。它有时被称为叠加栈(pushdown stack),因为最后“压入”(push)栈的元素,第一个被“弹出”(pop)栈。经常...

    JavaEdge
  • Java编程思想第五版(On Java8)(十一)-内部类

    内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可见性。然而必须要了解,内部类与组合是完全不同的概念,这一点很重要。在...

    JavaEdge
  • 设计模式(四) | 简历复印与原型模型不得不说的一些事

    谭庆波
  • spring-boot使用aop进行多数据源切换

    创建一个spring boot项目,并引入druid mysql aop等相关依赖

    用户1637228
  • RocketMQ详解(6)——Producer详解

    DefaultMQProducer是一个默认的消息生产者,可以支持发送普通消息和顺序消息。

    张申傲
  • 23种设计模式之——原型模式

    原型模式其实就是一个对象在创建另一个可定制的对象,而且不需要指定任何创建的细节。Java提供了Coneable接口,其中有一个唯一方法Clone(),实现这个接...

    良月柒
  • RestTemplate设置固定的url参数

    喜欢天文的pony站长
  • [设计模式] 适配器模式 + 外观模式

    顾名思义, ProductV2API是新的API接口, ProductV1API为历史API接口,实现类 HisAPIAdaptee为历史API的实现类,即被适...

    架构探险之道
  • 第五节 微服务OTRS 补充客户端负载均衡

    spring could在客户端负载均衡有两种选择一种是ribbon+restTemplate,另一种是feign。本主要讲解下基于ribbon+rest。当然...

    用户1418372

扫码关注云+社区

领取腾讯云代金券