源码走读-从JedisCluster的设计来发现对象池的奥秘
JedisCluster究竟是如何获得一个connection的?内部对象池又是如何工作的?
今天我们就去JedisCluster的源码看看,一探究竟。
好,先从JedisCluster开始。
JedisCluster
可以看到JedisCluster中除了构造函数之外,就是各种命令。建立连接是从构造函数开始,而JedisCluster作为一个子类一般是不会去直接去负责连接这件事情的,而是交给父类。所以我们去父类BinaryJedisCluster看看。
BinaryJedisCluster
可以发现,BinaryJedisCluster也没有去管新建连接这件事,而是交给了一个叫JedisClusterConnectionHandler的连接处理器去完成“连接”这件事情。
JedisClusterConnectionHandler
可以看到JedisClusterConnectionHandler把初始化连接的事交给了JedisClusterCache。
我们看到在构造函数的最后一个方法是initialSlotsCache。此方法负责把槽和传入的master节点初始化。就是用两个循环来完成初始化。现在我们去看看JedisClusterInfoCache。
JedisClusterInfoCache
通过上面的源码,我知道了传入的master列表最终被put进入了一个hashmap。key是"ip:port",value则是一个JedisPool。
接下来就看看JedisPool的实现吧。
JedisPool
我们发现,JedisPool并没有自己去实现对象池,而是直接使用了GenericObjectPool。而且是著名的apache commons包里的pool2。
现在去父类看看。
JedisPoolAbstract
Pool<Jedis>
可以发现此处使用了apache的pool2来实现的Jedis对象池。
好,现在就去看看GenericObjectPool的实现吧。
GenericObjectPool
在整个的构造函数里,我们只发现这么一个动态执行方法,其他的都是传入参数,这里应该就是初始化pool的入口,如上图:
this.startEvictor(this.getTimeBetweenEvictionRunsMillis());
现在去看看这个方法吧:
如图所述,我们现在就去BaseGenericObjectPool的Evictor类去看看这个任务的具体实现吧。
BaseGenericObjectPool的Evictor
可以看到Evictor果然是一个定时任务实现,里边有个关键调用是ensureMinIdle()方法,现在去看看这个方法吧:
可以看到ensureMinIdle()方法在BaseGenericObjectPool是一个抽象实现,现在就去子类GenericObjectPool。
GenericObjectPool
可以看到是通过LinkedBlockingDeque来存储批量创建好的一批对象。
LinkedBlockingDeque是双向链表实现的双向并发阻塞队列。该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(插入/删除);并且,该阻塞队列是支持线程安全。 此外,LinkedBlockingDeque还是可选容量的(防止过度膨胀),即可以指定队列的容量。如果不指定,默认容量大小等于Integer.MAX_VALUE。
以上就是初始化JedisCluster的过程以及初始化JedisPool的过程。
那么如何获取一个JedisCluster的连接呢?
上图中的获取随机的JedisPool列表的方法getShuffledNodesPool():
这也就是JedisCluster如何获取一个和redis服务端连接的过程。
那么如何从JedisPool中获取一个Jedis对象呢?
JedisPool通过getResource()来获取到Jedis对象。
通过internalPool的borrowObject方法来弹出一个实例供使用。
现在我们就去GenericObjectPool看看borrowObject(弹出对象)这个方法的实现。
GenericObjectPool-->borrowObject()
可以发现通过多个while循环最初弹出一个对象返回。
以上就是一个对象池的弹出对象的过程。
其实对象池算是一种设计模式,比较经典。网上也有很多它的实现。我们也可以从GenericObjectPool所实现的接口就可以看出对象池的一些基本的操作。
总结
通过上面的源码走读,我们知道了JedisCluster会随机获取一个master作为和redis服务端交互的客户端。
ps:由此我们也知道JedisCluster由于设置的都是master。所以当master出现问题时,要做故障转移,那么此时客户端就得重新启动,重新设置最新的master。所以如果我们要做故障转移的支持,可以尝试通过动态的修改nodes那个map里边的master来实现快速的无重启的故障转移。原生的JedisCluster是不支持无重启的故障转移的。
同时在JedisCluster初始化连接的过程中,内部使用JedisPool来初始化redis连接。
而JedisPool的实现又是通过apache commons下的pool2来实现的。而ObjectPool本身就是一个经典的设计模式。核心就是得在池里生成一批对象,就不用在每次调用时都去new一个Jedis。
你可以写个for循环去测试JedisPool和Jedis,据我的测试,性能简直天壤之别,这也正是对象池的美妙之处。
- 软件更新时候出现和原包名冲突
- 一个数字截取引发的精度问题(三)
- HTML生成PDF(c#)
- 一个数字截取引发的精度问题(二)
- 【独家推送】GoogLeNet构建技术分析因子的模式识别基于TensorFlow
- 一个数字截取引发的精度问题(一)
- 基于TLS1.3的微信安全通信协议mmtls介绍
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- Visual Studio 2012 中的ASP.NET Web API
- gradeview可拖动效果实现
- 【Python量化投资】拟合具有非平稳特征的神经网络对股票进行预测
- 使同事羡慕不已的8个npm命令
- JavaScript代码风格要素
- 从 WebAPI Beta 更新到WebAPI RC
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android ItemDecoration 实现分组索引列表的示例代码
- Android TextView实现词组高亮的示例代码
- Android开发使用json实现服务器与客户端数据的交互功能示例
- Android中实现词组高亮TextView方法示例
- AsyncTask类实例详解
- Android中ListView的item点击没有反应的解决方法
- android 应用内部悬浮可拖动按钮简单实现代码
- 详解Android PopupWindow怎么合理控制弹出位置(showAtLocation)
- Android Studio 3.0上分析内存泄漏的原因
- Android 实现图片生成卷角和圆角缩略图的方法
- Android使用TextInputLayout创建登陆页面
- Android使用WebSocket实现多人游戏
- Android Studio多渠道打包套路
- Vue路由History模式分析
- Android开发调用WebService的方法示例