文档章节

Jedis的Sharded源代码分析

绝地逢生
 绝地逢生
发布于 2017/06/01 10:13
字数 1212
阅读 3
收藏 0
点赞 0
评论 0

概述

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

继承关系

ShardedJedis--->BinaryShardedJedis--->Sharded <Jedis, JedisShardInfo>

构造函数

查看其构造函数

public ShardedJedis(List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
        super(shards, algo, keyTagPattern);
    }

构造器参数解释:

  • 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 connectionTimeout;
  private int soTimeout;
  private String host;
  private int port;
  private String password = null;
  private String name = null;
  // Default Redis DB
  private int db = 0;

  public String getHost() {
    return host;
  }

  public int getPort() {
    return port;
  }

  public JedisShardInfo(String host) {
    super(Sharded.DEFAULT_WEIGHT);
    URI uri = URI.create(host);
    if (JedisURIHelper.isValid(uri)) {
      this.host = uri.getHost();
      this.port = uri.getPort();
      this.password = JedisURIHelper.getPassword(uri);
      this.db = JedisURIHelper.getDBIndex(uri);
    } 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, timeout, Sharded.DEFAULT_WEIGHT);
  }

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

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

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

  public JedisShardInfo(URI uri) {
    super(Sharded.DEFAULT_WEIGHT);
    if (!JedisURIHelper.isValid(uri)) {
      throw new InvalidURIException(String.format(
        "Cannot open Redis connection due invalid URI. %s", uri.toString()));
    }

    this.host = uri.getHost();
    this.port = uri.getPort();
    this.password = JedisURIHelper.getPassword(uri);
    this.db = JedisURIHelper.getDBIndex(uri);
  }

@Override
  public Jedis createResource() {
    return new Jedis(this);
  }
    /**
    *    省略setters和getters
    **/
}

可见JedisShardInfo包含了一个redis节点ip地址,端口号,name,密码等等相关信息。要构造一个ShardedJedis,提供一个或多个JedisShardInfo。

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

public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
        this.algo = algo;
        this.tagPattern = tagPattern;
        initialize(shards);
    }

哈希环的初始化

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>();

    /**
     * The default pattern used for extracting a key tag. The pattern must have
     * a group (between parenthesis), which delimits the tag to be hashed. A
     * null pattern avoids applying the regular expression for each lookup,
     * improving performance a little bit is key tags aren't being used.
     */
    private Pattern tagPattern = null;
    // the tag is anything between {}
    public static final Pattern DEFAULT_KEY_TAG_PATTERN = Pattern.compile("\\{(.+?)\\}");

接下来看其构造函数中的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());
        }
    }

可以看到,它对每一个ShardInfo通过一定规则计算其哈希值,然后存到TreeMap中,这里它实现了一致性哈希算法中虚拟节点的概念,因为我们可以看到同一个ShardInfo不止一次被放到TreeMap中,数量是,权重*160。
增加了虚拟节点的一致性哈希有很多好处,能避免数据在redis节点间分布不均匀。

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

数据定位

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

例如其set方法:

public String set(String key, String value) {
        Jedis j = getShard(key);
        return j.set(key, value);
    }

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

public R getShard(byte[] key) {
        return resources.get(getShardInfo(key));
    }

    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方法从TreeMap中获得对应的ShardInfo,然后根据这个ShardInfo就能够再LinkedHashMap中获得对应的Jedis实例了

本文转载自:https://segmentfault.com/a/1190000002691429

共有 人打赏支持
绝地逢生
粉丝 0
博文 32
码字总数 13836
作品 0
浦东
程序员
Jedis的JedisSentinelPool源代码分析-监听切换事件

https://segmentfault.com/a/1190000002690506 本专栏与Redis相关的文章 Redis Sentinel机制与用法(一) Redis Sentinel机制与用法(二) Jedis的JedisSentinelPool源代码分析 Jedis的Shard...

毛朱
2016/08/24
107
0
Jedis 与 ShardedJedis 设计

Jedis设计 Jedis作为推荐的java语言redis客户端,其抽象封装为三部分: 对象池设计:Pool,JedisPool,GenericObjectPool,BasePoolableObjectFactory,JedisFactory 面向用户的redis操作封装...

tantexian
2016/06/07
49
0
jedis源码-分片路由

jedis分片相关的类有Sharded ShardInfo Jedis ShardInfo : Sharded : 当执行读写操作时要根据key从resources里取出一个jedis实例获取jedis实例分为两个步,重点是 通过key 从nodes节点里找到...

hehe_
2016/11/10
22
0
Spring Data Redis与Jedis的选择(转)

说明:内容可能有点旧,需要在业务上做权衡。 Redis的客户端有两种实现方式,一是可以直接调用Jedis来实现,二是可以使用Spring Data Redis,通过Spring的封装来调用。应该使用哪一个呢?基于...

easonjim
2017/11/08
0
0
redis连接集群 ERR unknown command 'SENTINEL'?

Java 连接redis集群报错提示 ERR unknown command 'SENTINEL'?怎么解决呢! 连接集群的代码在这里https://github.com/warmbreeze/sharded-jedis-sentinel-pool Exception in thread "main" ......

北极心
2016/10/21
1K
3
基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案

基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案 http://www.tuicool.com/articles/naeEJbv 基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案 时间 2014-02-21 15:15:17 IT......

毛朱
2015/08/30
0
0
Redis 的 sharded jedis 客户端实现 - sparrow-sharded-jedis

通过对 Redis 客户端的封装,从代码级别强制规范 key,使 Redis 方便管理和监控。 理论上框架可实现跨各种缓存的 db 的缓存层,且安全,友好,调用简洁。 功能列表: 进一步对客户端友好封装...

zh_harry
01/28
16
0
Jedis分片连接池(分布式)

一下内容来自网络,但是很多细节没有写出来,所以我经过自己琢磨,终于找到原因了。 Redis-2.4.15目前没有提供集群的功能,Redis作者在博客中说将在3.0中实现集群机制。目前Redis实现集群的方...

Zero零_度
2016/08/29
4
0
jedis2.1.0的一个bug

最近在使用jedis工程中,由于一些原因,使用的仍是较低版本的jedis版本的。使用jedis时图省事,直接通过new 一个jedis的对象使用。之后出现了ArrayIndexOutOfBoundsException的错误: 具体为...

kiss鱼肠
2015/09/28
287
0
MongoDB · 特性分析 · Sharded cluster架构原理

为什么需要Sharded cluster? MongoDB目前3大核心优势:『灵活模式』+ 『高可用性』 + 『可扩展性』,通过json文档来实现灵活模式,通过复制集来保证高可用,通过Sharded cluster来保证可扩展...

阿里云RDS-数据库内核组
2016/05/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

SpringBoot | 第十章:Swagger2的集成和使用

前言 前一章节介绍了mybatisPlus的集成和简单使用,本章节开始接着上一章节的用户表,进行Swagger2的集成。现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的...

oKong
今天
8
0
Python 最小二乘法 拟合 二次曲线

Python 二次拟合 随机生成数据,并且加上噪声干扰 构造需要拟合的函数形式,使用最小二乘法进行拟合 输出拟合后的参数 将拟合后的函数与原始数据绘图后进行对比 import numpy as npimport...

阿豪boy
今天
4
0
云拿 无人便利店

附近(上海市-航南路)开了家无人便利店.特意进去体验了一下.下面把自己看到的跟大家分享下. 经得现场工作人员同意后拍了几张照片.从外面看是这样.店门口的指导里强调:不要一次扫码多个人进入....

周翔
昨天
1
0
Java设计模式学习之工厂模式

在Java(或者叫做面向对象语言)的世界中,工厂模式被广泛应用于项目中,也许你并没有听说过,不过也许你已经在使用了。 简单来说,工厂模式的出现源于增加程序序的可扩展性,降低耦合度。之...

路小磊
昨天
171
1
npm profile 新功能介绍

转载地址 npm profile 新功能介绍 npm新版本新推来一个功能,npm profile,这个可以更改自己简介信息的命令,以后可以不用去登录网站来修改自己的简介了 具体的这个功能的支持大概是在6这个版...

durban
昨天
1
0
Serial2Ethernet Bi-redirection

Serial Tool Serial Tool is a utility for developing serial communications, custom protocols or device testing. You can set up bytes to send accordingly to your protocol and save......

zungyiu
昨天
1
0
python里求解物理学上的双弹簧质能系统

物理的模型如下: 在这个系统里有两个物体,它们的质量分别是m1和m2,被两个弹簧连接在一起,伸缩系统为k1和k2,左端固定。假定没有外力时,两个弹簧的长度为L1和L2。 由于两物体有重力,那么...

wangxuwei
昨天
0
0
apolloxlua 介绍

##项目介绍 apolloxlua 目前支持javascript到lua的翻译。可以在openresty和luajit里使用。这个工具分为两种模式, 一种是web模式,可以通过网页使用。另外一种是tool模式, 通常作为大规模翻...

钟元OSS
昨天
2
0
Mybatis入门

简介: 定义:Mybatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。 途径:MyBatis通过XML文件或者注解的形式配置映射,实现数据库查询。 特性:动态SQL语句。 文件结构:Mybat...

霍淇滨
昨天
2
0
开发技术瓶颈期,如何突破

前言 读书、学习的那些事情,以前我也陆续叨叨了不少,但总觉得 “学习方法” 就是一个永远在路上的话题。个人的能力、经验积累与习惯方法不尽相同,而且一篇文章甚至一本书都很难将学习方法...

_小迷糊
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部