ShardedJedisPipeline 源码分析
一、什么是pipeline?什么是ShardedJedis?
由于pipeline和ShardedJedis的介绍和源码分析在网上已经有了,本文就不再赘述,直接给出链接:
pipeline的介绍:
http://blog.csdn.net/freebird_lb/article/details/7778919
pipeline源码分析:
http://blog.csdn.net/ouyang111222/article/details/50942893
ShardedJedis :
http://blog.csdn.net/ouyang111222/article/details/50958062
请读者在继续阅读之前确保自己掌握了pipeline和shardedJedis的概念。
二、ShardedJedisPipeline源码分析
1:怎么使用?
如同名字一样,ShardedJedisPipeline是分布式异步调用的方式,即后端支持多台Redis实例,并且可以从客户端以pipeline的方式打包发送命令,先来看看怎么使用:
public static void main(String[] args) {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("IP1", 6379),
new JedisShardInfo("IP2", 6379),
new JedisShardInfo("IP3", 6379)
);
ShardedJedis shardedJedis = new ShardedJedis(shards);
ShardedJedisPipeline shardedJedisPipeline = shardedJedis.pipelined();
for (int i = 0; i < 10; i++) {
shardedJedisPipeline.set("k" + i, "v" + i);
}
shardedJedisPipeline.sync();
}
因为客户端有Hash算法,所以在for循环中set的k1~k9会被打散分配到三台机器上(为了模拟效果,也可以在同一台机器上启动三个Redis实例),下面是分别去三台机器上查看key的分布情况:
第一台:
127.0.0.1:6379> keys k*
1) "k2"
2) "k0"
第二台:
127.0.0.1:6379> keys k*
1) "k4"
2) "k5"
3) "k3"
4) "k9"
5) "k8"
第三台:
127.0.0.1:6379> keys k*
1) "k1"
2) "k6"
3) "k7"
如上所示,k1 ~ k9 分别在不同的机器上,我们接下来把数据拿回来:
for (int i = 0; i < 10; i++) {
shardedJedisPipeline.get("k"+i);
}
List<Object> list = shardedJedisPipeline.syncAndReturnAll();
for(Object obj:list) {
System.out.println(obj);
}
执行结果如下:
这时候难道不应该思考一个问题吗?
虽然我们get操作是依次 get k1 ~ k9 ,但是由于k1 ~ k9分别在不同的机器上,怎么保证他们回来的顺序呢?请在继续往下看之前先思考这个问题你会怎么解决。
2:开始分析
首先整一份Jedis的源码下来,推荐用IDEA打开,因为IDEA有功能可以生成类的调用图http://blog.csdn.net/qq_27093465/article/details/52857307,我生成的类图如下所示:
可以看到ShardedJedisPipeline继承自PipelineBase,继续继承自Queable。我们从get的代码开始,注意看我的注释,我保证以最简单的方式解释清楚这个问题:
shardedJedisPipeline.get("k"+i);
它的实现在PipelineBase中:
public Response<String> get(String key) {
this.getClient(key).get(key);
return this.getResponse(BuilderFactory.STRING);
}
我们接着去看看getClient(key) :
protected Client getClient(String key) {
/*getShard对key做HASH,同时返回这个key对应的client对象,一个client对象就代表了一条连接,此时返回的对象和set的时候后端对应的Redis机器IP和PORT是一样的,这样才能保证这条get命令发出去能去正确的机器上拿回数据*/
Client client = jedis.getShard(key).getClient();
/*!!! 关键点
private Queue<Client> clients = new LinkedList<Client>();
上面是clients的定义,是一个队列,它会按照client的使用顺序把它入队,相当于按照顺序保存了每个命令对应的连接(保存的本地端口是关键),因为回来的时候就按照这个顺序依次去端口读取数据了*/
clients.add(client);
results.add(new FutureResult(client));
return client; //最后把client返回
}
再回去看 this.getClient(key).get(key)
其实相当于调用 client.get(key)
,这样会把这条命令添加到outputstream
,但是不会发送,(因为是pipeline的方式,最后才会统一刷新输出流)this.getResponse(BuilderFactory.STRING)
相当于为每个回来的包准备一块空间。
接下来我们调用了:
List<Object> list = shardedJedisPipeline.syncAndReturnAll();
去看看syncAndReturnAll()
方法:
public List<Object> syncAndReturnAll() {
List<Object> formatted = new ArrayList<Object>();
/* 遍历clients 队列,按照先进先出的规则,依次从每个client对象拿出一条(getOne())返回结果。看下面的图解。
*/
for (Client client : clients) {
formatted.add(generateResponse(client.getOne()).get());
}
/*将结果添加到formatted返回*/
return formatted;
}
说明:
- 因为有三台Redis服务器,所以会有三条socket连接,假设他们对应的本地端口为3333,6666,9999,后面是每个连接的接收缓冲区。
- Redis服务器是单线程,所以每条连接上接收缓冲区返回的结果一定是按照顺序的,比如发送按照getk0,getk2的顺序,则结果也是按照这样返回。
- clients队列中记录了每个client对象,它能标识这条get命令应该去哪个本地端口读取数据,getone按照Redis协议分隔读取一条就是相应的结果
就这样依次出队,依次解析,现在我们假设队列读取到了最后的三条,则情况如下:
3:总结
其实这种方法很巧妙的原因也得益于Redis是一个单线程的服务器,对于发送向它的命令,总是按照发送的顺序返回,也正是这样,才能有pipeline这种方式,不然多线程各自都有自己的缓冲区,自己如果处理完就返回了,这样是没法玩的。
ShardedJedisPipeline 源码分析的更多相关文章
- Redis 专栏(使用介绍、源码分析、常见问题...)
一.介绍相关 说Redis : 介绍Redis特性,使用场景,使用Jedis操作Redis等. 二.源码分析 1. 数据结构 Redis源码分析(sds):Redis自己封装的C语言字符串类型. Re ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
- ABP源码分析二:ABP中配置的注册和初始化
一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...
随机推荐
- Java基础系列(9)- 数据类型扩展及常见面试题
整数拓展 // 整数拓展: 进制 二进制0b 十进制 八进制0 十六进制0x // 同一个数字在不同进制中,结果是不同的,进制换算 int i = 10; int i2 = 010; // 八进制 i ...
- Jmeter系列(30)- 性能指标(3) | 性能指标峰值
性能指标峰值 简述 彻底理解了性能指标(1)(2)的内容,这一篇随笔其实就不用看了,而且大家也能猜到这一篇内容是啥:二八原则 性能指标不要硬性的往那些性能指标上去靠,要根据业务来,熟悉业务,明白了解你 ...
- Shell系列(9)- 用户自定义变量(2)
定义变量 变量名=变量值 例如: x=123 mulu="当前目录下有 $(ls)" 备注: 变量名只能是字母.下划线.数字组成且不能以数字开头 变量等号两侧不能加空格 若变量值中 ...
- python学习笔记(十六)-Python多线程多进程
一.线程&进程 对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程, ...
- Python turtle.right与turtle.setheading的区别
一.概念 turtle.right与turtle.left用法一致,我们以turtle.right为例进行讲述. turtle.right(angle)向右旋转angle角度. turtle.seth ...
- 鸿蒙内核源码分析(重定位篇) | 与国际接轨的对外部发言人 | 百篇博客分析OpenHarmony源码 | v55.01
百篇博客系列篇.本篇为: v55.xx 鸿蒙内核源码分析(重定位篇) | 与国际接轨的对外部发言人 | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程 ...
- Winform 空闲时间(鼠标键盘无操作)
前言 Winform 在特定情况下,需要判断软件空闲时间(鼠标键盘无操作),然后在做一下一些操作. 实现 做了一个简单的例子,新建一个窗体,然后拖两个控件(Timer控件和label控件) using ...
- 【C++ Primer Plus】编程练习答案——第11章 (待更新)
最近开学,事情较多,过两天更新...
- 原生JS实现简单留言板功能
原生JS实现简单留言板功能,实现技术:css flex,原生JS. 因为主要是为了练手js,所以其中布局上的一些细节并未做处理. <!DOCTYPE html> <html lang ...
- 【.Net vs Java? 】 先来看一下Java和C#的数据类型区别。
新工作.Net和Java都要做,早期也做过一段Java的项目,但没有系统的深入学习过.一直觉得这两门语言估计是最相近的两门语言了,好多代码可以说直接拷过来都不带报错的,但仔细推敲还是有很多的不同. 1 ...