对于每个与服务器进行连接的客户端,服务器都为这些客户端建立了相应的src/server.h/client结构(客户端状态),这个结构保存了客户端当前的状态信息,以及执行相关功能时需要用的数据结构

创建客户端
  • 普通客户端

    • 创建普通客户端
      通过网络连接与服务器进行连接的普通客户端,那在客户端使用connect函数连接到服务器时,服务器就会调用连接应答处理器为客户端创建相应的客户端状态,并将添加到服务器状态结构clients链表的末尾

      源码:c = createClient(conn) => src/networking.c/createClient

    • 导致关闭普通客户端的原因

      • 客户端进程退出或者被杀死
      • 发送了不符合协议格式的命令请求
      • 使用client kill 命令关闭
      • 触发了timeout配置
      • 客户端发送的命令请求大小超过了输入缓冲区的限制大小(默认为1GB)
      • 服务端发送的命令回复大小超过了输出缓冲区的硬性限制大小或者软性限制大小
  • Lua脚本的伪客户端
    服务器会在初始化时创建负责执行Lua脚本中包含的Redis命令的伪客户端,并将这个伪客户端关联在服务器状态结构的lua_client属性

    lua_client伪客户端在服务器运行的整个生命周期中会一直存在,只有服务器被关闭,lua_client伪客户端才会被关闭

    源码:server.lua_client = createClient(NULL) => src/networking.c/createClient

  • AOF文件的伪客户端
    服务器会在载入AOF文件时,会创建用于执行AOF文件包含的Redis命令的伪客户端并载入完成之后,关闭这个伪客户端

    源码:src/aof.c/createAOFClient

客户端状态结构
// 客户端状态结构
// 客户端的属性包含通用属性 和 特定功能相关的属性
typedef struct client {
    // 客户端增量唯一ID
    uint64_t id;            
    // 客户端连接
    connection *conn;
    // RESP协议版本。可以是2或3
    int resp;               
    // 当前客户端目标数据库指针:记录客户端当前正在使用的数据库
    redisDb *db;            
    // 客户端的名字
    robj *name;             
    // 输入缓冲区:保存客户端发送的命令请求,不能超过1GB
    sds querybuf;           
    // 已读取到输入缓冲区的位置
    size_t qb_pos;          
    // 如果此客户端被标记为主服务器,则此输入缓冲区表示我们从主服务器接收的复制流中尚未应用的部分
    sds pending_querybuf;   
    // querybuf大小的近期峰值(100ms或以上)
    size_t querybuf_peak;   
    // 命令参数的个数:argv数组的长度
    int argc;               
    // 命令参数
    robj **argv;            
    // 参数被重写,原始命令的参数个数
    int original_argc;      
    // 参数被重写,原始命令的参数
    robj **original_argv;   
    // argv列表中对象的长度总和
    size_t argv_len_sum;    
    // 对应的命令结构
    struct redisCommand *cmd, *lastcmd;  
    // 可变大小的输出缓冲区:用于保存长度较大的回复,比如一个非常长的字符串值,一个由很多项组成的列表,一个包含了很多元素的集合等等,
    // reply属性大小受到client-output-buffer-limit配置的限制
    list *reply;            
    // 创建客户端的时间:用来计算客户端与服务器已经连接了多少秒
    time_t ctime;           
    // 客户端与服务器最后一次进行互动的时间(可以是客户端向服务器发送命令请求的时间,也可以是服务器向客户端发送命令回复的时间)
    // lastinteraction属性可以用来计算客户端的空转时间
    time_t lastinteraction; 
    // 输出缓冲区第一次到达软性限制的时间,用于对输出缓冲区大小超出软性限制,何时关闭客户端的参数
    time_t obuf_soft_limit_reached_time;
    // 客户端的角色标志和目前所处的状态,可以是单个标志,也可以是多个标志的二进制或
    uint64_t flags;         
    // 记录客户端是否通过了身份验证:0-未通过 1-已通过(默认值为0)
    // authenticated属性仅在服务器 启用 了身份验证功能时使用,如果服务器没有启用身份验证功能的话,那么即使authenticated属性的值为0,服务器也不会拒绝执行客户端发送的命令请求
    // 当authenticated属性的值为0时,除了AUTH命令以外,客户端发送的所有其他命令都会被服务器拒绝执行
    int authenticated;      
    // 固定大小的输出缓冲区:用于保存长度较小的回复,比如ok、简短的字符串值、整数值、错误回复等
    int bufpos; // 记录了buf数组目前已使用的字节数量
    char buf[PROTO_REPLY_CHUNK_BYTES]; // 回复数据字节数组,默认大小为16kb
} client;
索引

Redis 服务端程序实现原理

只言片语任我说,提笔句句无需忖。落笔不知寄何人,唯有邀友共斟酌。客户端 - 《Redis设计与实现》读书笔记_输入缓冲区