zoukankan      html  css  js  c++  java
  • Redis 客户端

      Redis 是一种一对多服务器程序,一个服务器可以被多个客户端连接,每个客户端向服务端发送请求,服务端处理请求并返回结果给客户端。

    但是 Redis 是以单线程的方式运行的,所以 Redis 通过使用 IO 多路复用技术实现的文件处理器,处理多个客户端的请求。这个后面会补充。

    这一节我们主要讲一下 Reids 客户端的一些属性。

    Redis 客户端属性

    对于每一个连接Redis服务器的客户端来说,服务器都会给客户端建立相应的 redis.h/redisClient 结构,这个结构保存了客户端当前的状体信息,以及执行相应功能时需要用到的数据结构。

    客户端的属性可以分为两类:

    • 通用属性,一般客户端建立连接都用得到的属性。
    • 特定功能相关的属性,比如执行事务时用到的 mstate 属性,执行 WATCH 命令时用到的 watched_keys 属性等等。

    本章主要介绍下客户端通用属性。

    套接字描述符

    客户端状态的 fd 属性记录了客户端正在使用的套接字描述符:

    typedef struct redisClient {
    
        // ...
    
        int fd;
    
        // ...
    
    } redisClient;

    根据客户端类型的不同, fd 属性的值可以是 -1 或者是大于 -1 的整数:

    • 伪客户端(fake client)的 fd 属性的值为 -1 : 伪客户端处理的命令请求来源于 AOF 文件或者 Lua 脚本, 而不是网络, 所以这种客户端不需要套接字连接, 自然也不需要记录套接字描述符。 目前 Redis 服务器会在两个地方用到伪客户端, 一个用于载入 AOF 文件并还原数据库状态, 而另一个则用于执行 Lua 脚本中包含的 Redis 命令。
    • 普通客户端的 fd 属性的值为大于 -1 的整数: 普通客户端使用套接字来与服务器进行通讯, 所以服务器会用 fd 属性来记录客户端套接字的描述符。 因为合法的套接字描述符不能是 -1 , 所以普通客户端的套接字描述符的值必然是大于 -1 的整数。

    可以看一下我本机客户端的 fd 属性:

    名字

    在默认情况下, 一个连接到服务器的客户端是没有名字的,可以看下我本机客户端 name 属性:

    可以使用 CIENT setname 命令为客户端设置一个名字,让客户端变得清晰一些:

     

     客户端的名字记录在客户端状态的 name 属性里面:

    typedef struct redisClient {
    
        // ...
    
        robj *name;
    
        // ...
    
    } redisClient;

    如果客户端没有为自己设置名字, 那么相应客户端状态的 name 属性指向 NULL 指针; 相反地, 如果客户端为自己设置了名字, 那么 name 属性将指向一个字符串对象, 而该对象就保存着客户端的名字。

    标志

    客户端的标志属性 flags 记录了客户端的角色(role),以及客户端目前所处的状态:

    typedef struct redisClient {
    
        // ...
    
        int flags;
    
        // ...
    
    } redisClient;

    flags 属性的值可以是单个标志,可以是多个标志的或,例如:

    # 客户端是一个主服务器
    REDIS_MASTER
    
    # 客户端正在被列表命令阻塞
    REDIS_BLOCKED
    
    # 客户端正在执行事务,但事务的安全性已被破坏
    REDIS_MULTI | REDIS_DIRTY_CAS
    
    # 客户端是一个从服务器,并且版本低于 Redis 2.8
    REDIS_SLAVE | REDIS_PRE_PSYNC
    
    # 这是专门用于执行 Lua 脚本包含的 Redis 命令的伪客户端
    # 它强制服务器将当前执行的命令写入 AOF 文件,并复制给从服务器
    REDIS_LUA_CLIENT | REDIS_FORCE_AOF | REDIS_FORCE_REPL

    输入缓冲区

    客户端状态的输入缓冲区用于保存客户端发送的命令请求:

    typedef struct redisClient {
    
        // ...
    
        sds querybuf;
    
        // ...
    
    } redisClient;

    其中 querybuf 是个 sds(简单动态字符串)类型的值,值的注意的是:

    输入缓冲区的大小会根据输入内容动态地缩小或者扩大, 但它的最大大小不能超过 1 GB , 否则服务器将关闭这个客户端。

    命令与命令行参数

    在服务器将客户端发送的命令请求保存到客户端状态的 querybuf 属性之后, 服务器将对命令请求的内容进行分析, 并将得出的命令参数以及命令参数的个数分别保存到客户端状态的 argv 属性和 argc 属性:

    typedef struct redisClient {
    
        // ...
    
        robj **argv;
    
        int argc;
    
        // ...
    
    } redisClient;

    argv 属性是一个数组, 数组中的每个项都是一个字符串对象: 其中 argv[0] 是要执行的命令, 而之后的其他项则是传给命令的参数。

    argc 属性则负责记录 argv 数组的长度,举个例子:

     

    则,上述结构在内存中的:

     命令的实现函数

    当服务器拿到客户端的 argv 属性和 argc 属性的值之后, 服务器将根据项 argv[0] 的值, 在命令表中查找命令所对应的命令实现函数。

    命令表, 该表是一个字典, 字典的键是一个 SDS 结构, 保存了命令的名字, 字典的值是命令所对应的 redisCommand 结构, 这个结构保存了命令的实现函数、 命令的标志、 命令应该给定的参数个数、 命令的总执行次数和总消耗时长等统计信息。

    当程序在命令表中成功找到 argv[0] 所对应的 redisCommand 结构时, 它会将客户端状态的 cmd 指针指向这个结构:

    typedef struct redisClient {
    
        // ...
    
        struct redisCommand *cmd;
    
        // ...
    
    } redisClient;

    之后, 服务器就可以使用 cmd 属性所指向的 redisCommand 结构, 以及 argv 、 argc 属性中保存的命令参数信息, 调用命令实现函数, 执行客户端指定的命令。

    输出缓冲区

    执行命令所得的命令回复会被保存在客户端状态的输出缓冲区里面, 每个客户端都有两个输出缓冲区可用, 一个缓冲区的大小是固定的, 另一个缓冲区的大小是可变的:

    • 固定大小的缓冲区用于保存那些长度比较小的回复, 比如 OK 、简短的字符串值、整数值、错误回复,等等。
    • 可变大小的缓冲区用于保存那些长度比较大的回复, 比如一个非常长的字符串值, 一个由很多项组成的列表, 一个包含了很多元素的集合, 等等。

    客户端的固定大小缓冲区由 buf 和 bufpos 两个属性组成:

    typedef struct redisClient {
    
        // ...
    
        char buf[REDIS_REPLY_CHUNK_BYTES];
    
        int bufpos;
    
        // ...
    
    } redisClient;

    buf 是一个大小为 REDIS_REPLY_CHUNK_BYTES 字节的字节数组, 而 bufpos 属性则记录了 buf 数组目前已使用的字节数量。

    REDIS_REPLY_CHUNK_BYTES 常量目前的默认值为 16*1024 , 也即是说, buf 数组的默认大小为 16 KB 。

    当 buf 数组的空间已经用完, 或者回复因为太大而没办法放进 buf 数组里面时, 服务器就会开始使用可变大小缓冲区。

    可变大小缓冲区由 reply 链表和一个或多个字符串对象组成:

    typedef struct redisClient {
    
        // ...
    
        list *reply;
    
        // ...
    
    } redisClient;

    通过使用链表来连接多个字符串对象, 服务器可以为客户端保存一个非常长的命令回复, 而不必受到固定大小缓冲区 16 KB 大小的限制。

    身份验证

    客户端状态的 authenticated 属性用于记录客户端是否通过了身份验证:

    typedef struct redisClient {
    
        // ...
    
        int authenticated;
    
        // ...
    
    } redisClient;

    如果 authenticated 的值为 0 , 那么表示客户端未通过身份验证; 如果 authenticated 的值为 1 , 那么表示客户端已经通过了身份验证。

    当客户端 authenticated 属性的值为 0 时, 除了 AUTH 命令之外, 客户端发送的所有其他命令都会被服务器拒绝执行。

    时间

    最后, 客户端还有几个和时间有关的属性:

    typedef struct redisClient {
    
        // ...
    
        time_t ctime;
    
        time_t lastinteraction;
    
        time_t obuf_soft_limit_reached_time;
    
        // ...
    
    } redisClient;

    ctime 属性记录了创建客户端的时间, 这个时间可以用来计算客户端与服务器已经连接了多少秒 —— CLIENT_LIST 命令的 age 域记录了这个秒数。

    lastinteraction 属性记录了客户端与服务器最后一次进行互动(interaction)的时间, 这里的互动可以是客户端向服务器发送命令请求, 也可以是服务器向客户端发送命令回复。

    lastinteraction 属性可以用来计算客户端的空转(idle)时间, 也即是, 距离客户端与服务器最后一次进行互动以来, 已经过去了多少秒。 CLIENT_LIST 命令的 idle 域记录了这个秒数。

    obuf_soft_limit_reached_time 属性记录了输出缓冲区第一次到达软性限制(soft limit)的时间。

    参考文献

    redis 设计与实现

    http://www.redis.cn/commands/client-list.html 

  • 相关阅读:
    我的知识库(4) java获取页面编码(Z)
    知识库(3)JAVA 正则表达式 (超详细)
    The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. Struts
    某人总结的《英语听力的技巧 》,挺搞的
    我的知识库(5)java单例模式详解
    构建可扩展程序
    SerialPort (RS232 Serial COM Port) in C# .NET
    Python学习笔记——String、Sequences
    UI题目我的答案
    jQuery学习系列学会操纵Form表单元素(1)
  • 原文地址:https://www.cnblogs.com/hulunbao/p/14199498.html
Copyright © 2011-2022 走看看