Jedis详解

因为工作的需要,底层同事对Redis进行了部分改造,增加了几个命令,对应着也就需要对Jedis进行部分修改,于是就把Jedis相关的代码读了一遍,发现其设计还是非常简单但又巧妙使用。

通常而言,我们对于Redis集群的操作通常来讲不会真正对应多个节点,而是由底层单独分片处理,换句话说我们应用程序对应的节点是一个,因此我们目前主要用的是JedisPool的方式,而很少会采用JedisCluster的方式。

下面的解释也主要是对上述的方式进行说明。

Jedis操作主要涉及到的类:JedisPool、Jedis、Client、BinaryClient、Connection等,下面是对其处理过程的简要记录。

1、Connection类是使用原生的Socket进行连接,下面是连接的代码:

从代码中可以很清晰的看到,使用的是原生Socket进行处理(换句话说,这种没有使用netty的方式不见得多稳定),另外我们也可以看到每个Connection对应了一个输入流和输出流,结合Redis是单线程的处理机制,我们会发现Jedis的处理很巧妙。

2、下面是JedisPool的定义:

我们发现它其实用的是Pool,Pool是啥?Pool其实是apache提供的一种池子的管理工具,像数据库连接池也有很多用这个的。

我们在使用JedisPool的时候通常是用getResource()方法获取到一个可操作的连接:

其实我们获取到的是一个Jedis对象,那么肯定有一个疑问,那就是Pool里面到底存储的是什么,它是怎么存储的?首先可以确认,肯定是存储的Jedis,从Pool这种泛型的使用方式也是可以看出的。那么在Pool对象里面是怎么存储Jedis对象的呢?

通过代码查看我们可以知道:其实最终存储在Pool里面的是:

SoftReference这个类熟悉的童鞋可能会了解,它是JDK自带的一个类,说的比较玄乎,是一个软连接的类,其实就是说这个对象引用不是强引用,比较容易被GC掉。

SoftReference这个类继承自Reference,我们跟踪一下会发现最终存储的是一个Queue:

我们再回来深入看一下getResource的代码,也就是看一下super.getResource(),可以看出最终其实是由apache的pool实现的:

同时,我们发现其实还有一个和它并列的方法:

这样我们就可以猜测,其实是一个队列的获取操作,那么里面的参数(borrowMaxWaitMillis)也就是借用时间了,换句话说就是多长时间可以从队列中获取到对象。下面是这部分的主要实现代码:

代码太多,就不全部截取了,其中p只是一个临时变量,实际的队列其实是idleObjects,它是一个链表实现的双向阻塞队列:

3、获取到Jedis对象之后,剩下的其实就是Jedis的操作,以set方法为例:

我们会发现它主要是两个操作:一个是set,一个是getStatusCodeReply。这就是我们所说的Jedis设计巧妙之处,根据我们一般的理解,都是发送一个命令,然后等待应答,换句话说就是阻塞的处理方式。我们从Jedis的处理中发现,其实不是,它的发送和它的接收是分开处理的,当然这种分开处理指的是代码,其实本质上并没有分开。

首先看一下set命令的处理:

Jedis中set命令其实是Client执行的过程,而Client的处理其实是调用的BinaryClient的处理,BinaryClient都是使用byte[]与Redis进行交互,因此更具有普遍性。下面是BinaryClient的处理过程:

它调用的是sendCommand方法,该方法是由最开始我们建立的那个Connection实现的,如下:

从代码中我们可以看出,其实最终调用的是Protocol.sendCommand命令,如下:

RedisOutputStream其实也是一种流,它继承自FilterOutputStream,主要是改动就是为了适应Redis的协议,增加了一些变量的处理:

同时,为了保证安全期间,它被定义为一个final类,不允许被继承。

接着上述的Protocol.sendCommand命令,这个命令的执行其实没有flush,也就是说可能会出现写入不完整的问题,作者肯定不会犯这种低级的错误,所以在后面的那个getStatusCodeReply方法中,它是这样实现的:

它首先flush了一下,以保证其全部写入,然后是进行read,readProtocolWithCheckingBroken()方法如下:

最终还是调用的Protocol,也就是对输入流的一种读取操作。

最终从InputStream中读出的数据就是应答的结果。Jedis在实现的时候区分了多种应答情况,有的应答是状态(OK)、(NIL)、(ERROR)等,有的应答是数字,也有的应答是结果(Value),对应着不同的方法。

 

上面就是Jedis的基本处理过程,其实很简单,相信大家看了之后也可以自己写一个了,下面附上几张Jedis中的UML图,其中带ExtensionXXX的是我因为需求加的,源码中肯定没有。

Jedis:

Client:

BinaryClient

你可能感兴趣的:(redis)