Jedis源码分析(一)-Jedis介绍

  1. Jedis源码分析(一)-Jedis介绍
  2. Jedis源码分析(二)-Jedis类结构及实现
  3. Jedis源码分析(三)- JedisCluster类结构及实现
  4. Jedis源码分析(四)-JedisSentinel与ShardedJedis介绍

1 Jedis的类结构

​ 首先看Jedis的内部结构,图2-1中用橘色框标出了主要支架, 为突出主要架构,或有稍许内容没有标出。java

图片描述

图1-1 Jedis的类结构

​ Jedis以输入的命令参数是否为二进制,将处理请求的具体实现部署在两个类中,例如JedisBinaryJedisClientBinaryClient。与Redis服务器的链接信息(Socket,host,port……)封装在Client的基类Connection中。BinaryJedis类中提供了Client,Pipeline和Transcation变量,对应3种请求模式。redis

2 Jedis的初始化流程

Jedis jedis = new Jedis("localhost", 6379, 15000);
Transaction t = jedis.multi();
Pipeline pipeline = jedis.pipelined();

​ Jedis经过传入Redis服务器地址(host,port)开始初始化,而后在BinaryJedis里实例化Client。Client经过Socket维持客户端与Redis服务器的链接与沟通。
前文提到Transaction和Pipeline很类似,它们继承同一个基类MultiKeyPipelineBase。区别在于Transaction在实例化的时候,就自动发送MULTI命令,开启事务模式,而Pipeline则按状况手动开启,它们均依靠Client发送命令。如下是Transaction和Pipeline初始化的具体实现:segmentfault

//BinaryJedis类
public Transaction multi() {
client.multi();
transaction = new Transaction(client);
return transaction;
}
public Pipeline pipelined() {
pipeline = new Pipeline();
pipeline.setClient(client);
return pipeline;
}

​ 下面经过发送一个get key 的命令,看看,这三种模式是如何运转的。服务器

3 Jedis工做模式的调用流程

3.1 client请求模式

​ 以get key 为例,为突出主要步骤,部分代码略有缩减。
用法:架构

jedis.get("foo");

图片描述
图3-1 Clinet模式的时序图 socket

图片描述
图3-2 Client模式的调用流程工具

​ 图3-1和3-2是client模式下,发送请求,读取回复的具体实现。能够清楚看到,在每次发送命令前,会先经过connect()方法判断是否已经链接,若未链接则进行以下操做:源码分析

  1. 实例化Socket,并配置,
  2. 链接Socket,获取OutputStream和InputStream
  3. 若是是SSL链接,则会经过SSLSocketFactory建立socket链接

​ Protocol是一个通信工具类,将Redis的各种执行关键字存储为静态变量,能够直观调用命令,例如Protocol.Command.GET。同时,将命令包装成符合Redis的统一请求协议,回复消息的处理也是在这个类进行,先经过通信协提取出当次请求的回复消息,将Object类型的消息,格式化为String,List等具体类型,若是回复消息有Error则以异常的形式抛出。ui

3.2 Pipeline和Transaction模式

图片描述
图3-3 Transaction和Pipeline的类结构

​ 图3-3 是Transaction和Pipeline两个类的的类结构。能够看到Pipeline和Transaction都继承自MultiKeyPipelineBase,其中,MultiKeyPipelineBasePipelineBase的区别在于处理的命令不一样,内部均调用Client发送命令。从如下用例也能够看出二者的操做也十分相似。Pipeline有一个内部类对象MultiResponseBuilder,前文提到,当Redis事务结束时,会以List的形式,一次性返回全部命令的执行结果。MultiResponseBuilder对象就是用于,当Pipeline开始其实模式后,在事务结束时,存储全部返回结果。
Queable用一个LinkedList装入每一个命令的返回结果,Response<T>是一个泛型,set(Object data)方法传入格式化以前的结果,get()方法返回格式化以后的结果。
Pipeline的使用方法:

Pipeline p = jedis.pipelined();
//只发送命令,不读取结果,此时的Response<T>没有数据
Response<String> string = p.get("string");
Response<String> list = p.lpop("list");
Response<String> hash = p.hget("hash", "foo");
Response<Set<String>> zset = p.zrange("zset", 0, -1);
Response<String> set = p.spop("set");
//一次读取全部response.此时的Response<T>有数据
p.sync();

assertEquals("foo", string.get());
assertEquals("foo", list.get());
assertEquals("bar", hash.get());
assertEquals("foo", zset.get().iterator().next());
assertEquals("foo", set.get());

Transactions使用方法:
//开启事务
Transaction t = jedis.multi();
//命令进入服务端的待执行队列
Response<String> string = t.get("string");
Response<String> list = t.lpop("list");
Response<String> hash = t.hget("hash", "foo");
Response<Set<String>> zset = t.zrange("zset", 0, -1);
Response<String> set = t.spop("set");
//发送EXEC指令,执行全部命令,并返回结果
t.exec();

assertEquals("foo", string.get());
assertEquals("foo", list.get());
assertEquals("bar", hash.get());
assertEquals("foo", zset.get().iterator().next());
assertEquals("foo", set.get());

图片描述
图3-4 Pipeline的调用时序图
图片描述
图3-5 Pipeline的调用流程

​ 图3-4,3-5 显示了Pipeline从发送请求到读取回复的具体实现,Transaction的实现与其相似,于是没有另外作图说明。由上图可见,Pipeline经过Client发送命令,Client在sendCommand时,会同时执行pipelinedCommands++,记录发送命令的条数(参见图3-5)。以后,返回一个Response<T>实例,并将这个实例塞入了pipelinedResponses队列中。Response<T>主要有3个属性:

  1. 格式化前的回复消息data,
  2. 格式化后的回复消息response,
  3. 格式化方式builder。

​ 刚发送消息后,Response<T>里面的dataresponse是空值,只有格式化的方式builderSync()用于一次性读取全部回复,首先调用client的getAll()方法,getAll()方法根据以前记录的pipelinedCommands和Redis通信协议,读取相同条数的回复消息到一个List,并返回给Pipeline。随后遍历这个List,逐个将回复消息赋给pipelinedResponses中每一个Response<T>data

​ 在执行Response<T>.get()命令时,Response<T>里面data已经有值了,可是是Object类型的,于是还要调用build()方法,作一次数据转换,返回格式化以后的数据。

以上就是Pipeline的主要工做流程。Transaction的exec()方法和sync()很类似,下文为exec()的具体实现。

public List<Object> exec() {
  // 清空inputstream里面的全部数据,忽略QUEUED or ERROR回复
  client.getMany(getPipelinedResponseLength());
  //发送EXEC指令,让服务端执行全部命令
  client.exec();
  //事务结束
  inTransaction = false;
  //从inputStream中读取全部回复
  List<Object> unformatted = client.getObjectMultiBulkReply();
  if (unformatted == null) {
    return null;
  }
  //和sync()同样
  List<Object> formatted = new ArrayList<Object>();
  for (Object o : unformatted) {
    try {
     formatted.add(generateResponse(o).get());
    } catch (JedisDataException e) {
    formatted.add(e);
    }
  }
  return formatted;
}

猜你喜欢

转载自blog.csdn.net/kongliand/article/details/114637960