文档章节

Jedis源码分析:JedisClusterConnectionHandler

o
 osc_g8254g7s
发布于 2019/08/19 17:22
字数 1506
阅读 32
收藏 0

精选30+云产品,助力企业轻松上云!>>>

JedisClusterConnectionHandler

   JedisClusterConnectionHandler提供了JedisCluster接口获取资源池中Jedis连接对象的一个门面类,JedisClusterConnectionHandler提供了初始化集群,获取资源池中连接对象,刷新资源池等各种方法。JedisClusterConnectionHandler必须依赖于JedisClusterInfoCache类,因为 JedisClusterConnectionHandler本身的方法都是基于JedisClusterInfoCache类实现的,下列所讲解的方法的底层实现会在JedisClusterInfoCache进行讲述

   JedisClusterConnectionHandler实例化时必须提供以下的参数(见代码1-1),这些参数本身对于JedisClusterConnectionHandler没什么用处,完全就是在该类的构造器中走了一遭,因为JedisClusterConnectionHandler内部维护着JedisClusterInfoCache实例。JedisClusterConnectionHandler将这些参数传递给cache实例后,通过initializeSlotsCache()方法获取集群信息,并调用cache.discoverClusterNodesAndSlots()将获取到的集群信息分别放入nodes缓存和slots缓存中。这两个缓存的存储形式为 Map<String, JedisPool>,Map<Integer, JedisPool> ,虽然都是Map,但是存储的数据内容完全不同,nodes存储着集群所有节点信息,存储形式为<"host:port",JedisPool>,而slots存储的信息为<slot,JedisPool>,并且slots只存储主节点信息。

   从代码1-2中看出,通过遍历节点集合,实例化Jedis对象,再调用cache.discoverClusterNodesAndSlots(jedis)方法将节点放入缓存Map中,获取集群信息的方法也很简单,通过发送'cluster slots'便知道槽和节点的对应情况,再将Redis服务端返回来的输入流解析成Java对象即可。这里注明下,发现集群状况只需要一个可用的Jedis实例即可,但是集群节点总会发生"挂掉"的情况,建议在使用JedisCluster时配上所有的节点信息。

代码1-1

public JedisClusterConnectionHandler(Set<HostAndPort> nodes,
      final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName,
      boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
      HostnameVerifier hostnameVerifier, JedisClusterHostAndPortMap portMap) {
      //实例化集群信息缓存
    this.cache = new JedisClusterInfoCache(poolConfig, connectionTimeout, soTimeout, password, clientName,
        ssl, sslSocketFactory, sslParameters, hostnameVerifier, portMap);

    initializeSlotsCache(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
  }

 

代码1-2 集群发现及初始化缓存池

 private void initializeSlotsCache(Set<HostAndPort> startNodes, GenericObjectPoolConfig poolConfig,
      int connectionTimeout, int soTimeout, String password, String clientName,
      boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) {
    //遍历节点集合,拿出一个可用的节点获取集群信息
      for (HostAndPort hostAndPort : startNodes) {
      Jedis jedis = null;
      try {
        jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
        //添加密码认证
        if (password != null) {
          jedis.auth(password);
        }
        //设置客户端名称
        if (clientName != null) {
          jedis.clientSetname(clientName);
        }
        //TODO 根据节点发现集群信息
        cache.discoverClusterNodesAndSlots(jedis);
        break;
      } catch (JedisConnectionException e) {
        // try next nodes
      } finally {
        if (jedis != null) {
          jedis.close();
        }
      }
    }
  }

 

集群节点的获取

   JedisClusterConnectionHandler提供了四种获取可用节点的方法,其中 getConnection()和 getConnectionFromSlot(int slot);被定义为抽象方法,交由JedisSlotBasedConnectionHandler子类进行实现。值得注意的是,在获取Jedis连接时,有两个比较重要的点需要说明一下。前面说过JedisClusterInfoCache存放着nodes和slots两个map对象,通常集群模式都会做主从分离,即master节点负责读写操作,slave节点负责读和备份.如果尝试对slave节点进行写操作,那么会发生写入错误。所以提供了getConnectionFromSlot(int slot) 方法来根据槽点值获取对应的主节点信息,如果此时后端集群发生主从切换,该方法会尝试刷新集群状态来保证获取可用的Jedis对象。如果集群频繁的出现切换,那么即使刷新了集群信息也不能保证获取到的Jedis对象一定是有效的,代码中也做了说明"It can't guaranteed to get valid connection because of node assignment(因为节点的分配问题并不能获取到有效连接)",所以代码中采用了重试机制和随机数来尽量保证获取到的是有效的Jedis连接。第二个点是Jedis包依赖于Apache-CommonsPool2来做Jedis的二级缓存,为了保证Jedis是有效的,Jedis包下的JedisFactory实现了PooledObjectFactory相关方法,通过配置DEFAULT_TEST_ON_CREATE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_NUM_TESTS_PER_EVICTION_RUN 参数保证在创建阶段,借用阶段,归还阶段时Jedis对象都是可用的。

  • Jedis getConnectionFromNode(HostAndPort node)  使用二级缓存,先从JedisClusterInfoCache中获取缓存对象,再去commons-pool包下获取Jedis对象,如果commons-pool2中没有存储该对象,那么先将该对象放入通用缓存池中再进行获取。
  • Map<String, JedisPool> getNodes() 直接从JedisClusterInfoCache 的nodes缓存中获取所有JedisPool对象
  • Jedis getConnection() 遍历nodes中的节点,如果存在一个jedis对象能够成功执行ping命令,那么返回该Jedis对象。如果遍历完所有的node节点仍然不能找到一个有效的节点,那么抛出JedisNoReachableClusterNodeException异常
  • Jedis getConnectionFromSlot(int slot) 根据槽点值获取对应的主节点信息,slots缓存map中存放着槽点值->master node的对应关系。

代码2-1:从缓存池中获取Jedis对象

public Jedis getConnectionFromNode(HostAndPort node) {
      //使用二级缓存,先从JedisClusterInfoCache中获取缓存对象,再去commons-pool包下获取Jedis对象
    return cache.setupNodeIfNotExist(node).getResource();
  }

  public Map<String, JedisPool> getNodes() {
      //直接从JedisClusterInfoCache 的nodes缓存中获取所有JedisPool对象
    return cache.getNodes();
  }

 public Jedis getConnection() {
    // In antirez's redis-rb-cluster implementation,
    // getRandomConnection always return valid connection (able to
    // ping-pong)
    // or exception if all connections are invalid
      //随机获取
    List<JedisPool> pools = cache.getShuffledNodesPool();

    /**
     * 遍历缓存池中的每个池对象
     */
    for (JedisPool pool : pools) {
      Jedis jedis = null;
      try {
        jedis = pool.getResource();
        if (jedis == null) {
          continue;
        }
        String result = jedis.ping();//尝试进行ping
        if (result.equalsIgnoreCase("pong")) return jedis;//返回可用连接

        jedis.close();
      } catch (JedisException ex) {
        if (jedis != null) {
          jedis.close();
        }
      }
    }
    //遍历完成后仍然无法获取有效实例
    throw new JedisNoReachableClusterNodeException("No reachable node in cluster");
  }

  public Jedis getConnectionFromSlot(int slot) {
      //从slots中获取
    JedisPool connectionPool = cache.getSlotPool(slot);
    if (connectionPool != null) {
      // It can't guaranteed to get valid connection because of node
      // assignment
      return connectionPool.getResource();
    } else {
        //刷新集群信息
      renewSlotCache(); //It's abnormal situation for cluster mode, that we have just nothing for slot, try to rediscover state
      connectionPool = cache.getSlotPool(slot);
      if (connectionPool != null) {
          //TODO
        return connectionPool.getResource();
      } else {
        //no choice, fallback to new connection to random node
          //回到随机获取
        return getConnection();
      }
    }
  }

 

集群信息的重置与关闭

JedisClusterConnectionHandler提供了三个方法来进行集群信息的刷新和关闭操作,详细的说明见《Jedis源码分析:JedisClusterInfoCache》

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
redis集群下使用pipline进行批处理操作(spring/springboot+jedis)

我们都知道redis集群下对于mget、mset、pipeline、事务的支持不太好。 当然对于mget和mset有这么几种方法: 1、串行遍历key依次执行(这种就是把批量拆开了) 2、使用hash_tag包装key,在计算...

哥本哈根的小哥
2019/07/09
482
0
Jedis cluster集群初始化源码剖析

在项目中我们经常使用spring-data-redis来操作Redis,它封装了Jedis客户端来与Redis服务器进行各种命令操作。由于最近用到了Redis Cluster集群功能,这里就分析总结一下Jedis cluster集群初始...

九州暮云
2017/10/30
1.8K
0
一种简单实现Redis集群Pipeline功能的方法及性能测试

上一篇文章《redis pipeline批量处理提高性能》中我们讲到redis pipeline模式在批量数据处理上带来了很大的性能提升,我们先来回顾一下pipeline的原理,redis client与server之间采用的是请求...

osc_3xmkn220
04/16
9
0
REDIS FAQ

安装 https://github.com/ServiceStack/redis-windows RedisManager连接指定ip的redis server 修改6379.conf 改为 生产环境启动停止 测试连接是否正常 多数据库 redis默认包含16个数据库 以数...

无畏的老巨人
2017/08/23
20
0
Jedis源码浅析

1、概述 Jedis是redis官网推荐的redis java client,代码维护在github https://github.com/xetorthio/jedis。 本质上Jedis帮我们封装了各种redis命令,提供了各种和redis命令相关的方法使用。...

osc_lfnnqo7g
2019/11/07
2
0

没有更多内容

加载失败,请刷新页面

加载更多

平时使用的Lszrz到底是什么协议?说说Xmodem/Ymodem/Zmodem

XMODEM, YMODEM, and ZMODEM 由于平时使用rz/sz较多,r/s好理解,一个send一个receive。但是由不太清楚z是什么意思,故有此文。 sx/rx, sb/rb (b=batch)和sz/rz分别实现了xmodem,ymodem和z...

独钓渔
今天
17
0
真正的强智能时代已经到来。道翰天琼认知智能机器人平台API大脑。

最近,我常说人工智能的寒冬快要来了,提醒业界要做好思想准备,但同时我也说:冬天来了,春天就不会远了…… 2019年6月我写了篇文章《深度学习的问题究竟在哪?》,说到深度学习的一个主要问...

jackli2020
今天
24
0
什么是控制型人格,控制型人格的筛查测试

一、 什么是控制性人格 拥有控制型人格的人,他们会尽力的隐藏自己的意图,但是又会使用很微妙的方式来利用周围人的弱点,进而占取便宜时,使他们能够得到自己想要的东西。这类人的控制欲非常...

蛤蟆丸子
今天
14
0
【Spring】Spring AOP 代理对象生成逻辑源码分析

1. spring aop案例(POJO注入) 1.0 被代理接口 TargetInterface /** * 被代理的接口 * @author Yang ZhiWei */public interface TargetInterface { void show(); String show......

ZeroneLove
今天
36
0
聊聊dubbo-go的gracefulShutdownFilter

序 本文主要研究一下dubbo-go的gracefulShutdownFilter gracefulShutdownFilter dubbo-go-v1.4.2/filter/filter_impl/graceful_shutdown_filter.go type gracefulShutdownFilter struct {......

go4it
今天
30
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部