美文网首页
Redis阻塞操作实现原理

Redis阻塞操作实现原理

作者: Haiger | 来源:发表于2016-12-30 03:47 被阅读0次

    最近一位朋友问到:既然Redis是单线程的工作模式,那像BLPOP这样的阻塞操作又是然后实现的呢?

    接下来分别从服务端和客户端来阐述这一逻辑的实现原理。

    Redis Server:
    redis实现了一套事件触发模型,主要处理两种事件:I/O事件(文件事件)和定时事件。而处理它们的就靠一个EventLoop线程。同时redis还提供了丰富的数据结构,今天我们要分析的主要是List数据结构中的阻塞命令。
    先来看看BLPOP的源码(做了精简,只看主要的部分,详细的可以看文尾提供的参考链接):
    t_list.c

    t_list.c_1.png
    t_list.c_2.png
    上面代码表明:如果客户端发来一个blpop key命令,redis先找到对应的key的list,如果list不为空则pop一个数据返回给客户端;如果对应的list不存在或者里面没有数据,就将该key添加到一个blockling_keys的字典中,value就是想订阅该key的client链表。此时对应的client的为block状态,且i/o channel里面没有写入数据。

    既然是list数据结构,当然有push数据的操作:
    同样是t_list.c

    t_list.c_3.png
    t_list.c_4.png
    上面代码表明:如果客户端发来一个repush key value命令,先从blocking_keys中查找是否存在对应的key,如果存在就往ready_keys这个链表中添加该key;同时将value插入到对应的list中,并响应客户端。

    从上面的分析来看,主要是blocking_keys和ready_keys的作用,那何时才会处理它们呢? 我们知道redis全靠EventLoop来处理所以的I/O事件,我们来看看所以命令的处理入口:
    redis.c

    redis.c_1.png
    t_list.c_5.png
    t_list.c_6.png
    上面代码表明:每次处理完客户端命令后都会遍历ready_keys,并通过blocking_keys找到对应的client,依次将对应list的数据pop出来并响应对应的client;同时检查是否需要再次block。

    这样一来整个流程就清晰了。redis就是通过blocking_keys和ready_keys两个数据结构来实现的阻塞操作。但整个阻塞并没有阻塞EventLoop本身,从而实现命令的快速响应。算是一个典型的空间换时间的设计思路。

    接下来再看看客户端如何实现一个阻塞的I/O请求。
    Client:
    这里我们分两种I/O模型来阐述:阻塞I/O(BIO)和非阻塞I/O(NIO)。
    BIO,以Jedis为例。
    BinaryJedis.java

    BinaryJedis.java.png
    可以看出一个blpop请求会向redis发起两次I/O请求,一次向redis发送BLPOP key命令,一次从对应的链接管道(channel)中读取数据。由于BIO的特性当channel中没有数据时会一直阻塞,直到有新数据为止。这样就实现了客户端的阻塞效果。
    注意:这里的链接是被独享的,不然会有数据干扰。

    NIO的实现就稍微复杂一些,这里分两种情况(以netty为例):
    不带RquestID的实现方式
    伪代码如下:


    不带RquestID.png

    由于NIO的特性read和write是两个I/O事件,要分别等待selector来触发,所以不能像BIO那样连续发起两次I/O操作。再加上没有requesID,当read到数据时无法找到之前对应的发起者,所以这里的链接也必须是独享的,同时由一个只能包含一个元素的阻塞队列LinkedBlockingQueue来实现阻塞的效果。

    带RequestID的实现方式
    伪代码如下:


    带RequestID.png

    这里因为reqeust和response数据结构里都有带上了requestId,并且在链接对象上缓存了requestId和响应future的对应关系,因此链接可以不用独享。

    到处,整个阻塞的实现原理分析完毕。

    参考链接:
    带注解的redis源码:
    https://github.com/huangz1990/annotated_redis_source/blob/unstable/src/t_list.c https://github.com/huangz1990/annotated_redis_source/blob/unstable/src/redis.c
    IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)
    http://blog.csdn.net/historyasamirror/article/details/5778378

    相关文章

      网友评论

          本文标题:Redis阻塞操作实现原理

          本文链接:https://www.haomeiwen.com/subject/svjxtttx.html