zoukankan      html  css  js  c++  java
  • Redis阻塞操作实现原理(转)

    原文:https://www.jianshu.com/p/xsMzfn

    作者:Haiger

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

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

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

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

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

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

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

    上面代码表明:每次处理完客户端命令后都会遍历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

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

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

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

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

    这里因为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

  • 相关阅读:
    为什么有时候程序出问题会打印出“烫烫烫烫...
    VC++共享数据段实现进程之间共享数据
    IEEE浮点数float、double的存储结构
    前端智勇大闯关
    Python:高级主题之(属性取值和赋值过程、属性描述符、装饰器)
    来认识下less css
    Koala Framework
    在使用Kettle的集群排序中 Carte的设定——(基于Windows)
    标准库类型
    iOS多线程的初步研究1
  • 原文地址:https://www.cnblogs.com/ajianbeyourself/p/15650518.html
Copyright © 2011-2022 走看看