【Redis】 客户端

Metadata

title: 【Redis】 客户端
date: 2023-07-09 08:41
tags:
  - 行动阶段/完成
  - 主题场景/数据存储
  - 笔记空间/KnowladgeSpace/ProgramSpace/BasicsSpace
  - 细化主题/数据存储/Redis
categories:
  - 数据存储
keywords:
  - 数据存储/Redis
description: 【Redis】 客户端

概述

Redis 服务器的状态结构 clients 属性是链表,记录了所有与服务器相连的客户端结构,对客户端执行批量操作或查找操作,都可以通过 clients 链表完成:

struct redisServer{
    //一个链表,保存了所有客户端状态
    list *clents
    ...
};

  • 服务器状态结构使用clients链表连接起多个客户端状态,新添加的客户端状态会被放到链表的末尾。
  • 客户端状态的flags属性使用不同标志来表示客户端的角色,以及客户端当前所处的状态。
  • 输入缓冲区记录了客户端发送的命令请求,这个缓冲区的大小不能超过1GB。
  • 命令的参数和参数个数会被记录在客户端状态的argv和argc属性里面,而cmd属性则记录了客户端要执行命令的实现函数。
  • 客户端有固定大小缓冲区和可变大小缓冲区两种缓冲区可用,其中固定大小缓冲区的最大大小为16KB,而可变大小缓冲区的最大大小不能超过服务器设置的硬性限制值。
  • 输出缓冲区限制值有两种,如果输出缓冲区的大小超过了服务器设置的硬性限制,那么客户端会被立即关闭;除此之外,如果客户端在一定时间内,一直超过服务器设置的软性限制,那么客户端也会被关闭。
  • 当一个客户端通过网络连接连上服务器时,服务器会为这个客户端创建相应的客户端状态。网络连接关闭、发送了不合协议格式的命令请求、成为CLIENT KILL命令的目标、空转时间超时、输出缓冲区的大小超出限制,以上这些原因都会造成客户端被关闭。
  • 处理Lua脚本的伪客户端在服务器初始化时创建,这个客户端会一直存在,直到服务器关闭。
  • 载入AOF文件时使用的伪客户端在载入工作开始时动态创建,载入工作完毕之后关闭。

客户端属性

客户端的属性主要分为通用和特定的,这里主要介绍通用的。简单来说有套接字描述符,标志,输入缓冲区,命令与参数,输出缓冲区,时间等。

typedef struct redisClient{
    //套接字描述符
    int fd;
    //标志
    int flags;
    //输入缓冲区
    sds querybuf;
    //单个命令拆分的数组
    robj **argv;
    //argv数组的长度
    int argc;
    //命令函数
    struct redisCommand *cmd;
    //固定大小输出缓冲区,默认16K
    char buf[REDIS_REPLY_CHUNK_BYTES];
    //buf已使用字节数
    int bufpos;
    //大小可变输出缓冲区
    list *reply
    //创建客户端的时间
    time_t ctime;
    //与服务器互动的最后时间
    time_t lastinteraction;
    //软性限制时间
    time_t obuf_soft_limit_reached_time;
    ...
}redis client;

套接字描述符 fd

根据客户端类型不同:

  • fd 为 - 1 表示伪客户端。
  • fd 为大于 - 1 的整数时表示普通客户端。

伪客户端就是用于处理的命令请求来源于 AOF 或 Lua 脚本,不需要套接字连接,也就不需要套接字记录符。普通客户端就是所有来源于网络需要套接字连接的客户端。

标志 flags

标志 flags 记录了客户端的角色。有主从标志,Lua 伪客户端标志,执行 MONITOR 标志… 标志可以以二进制来拼接:flags:<flag1>|<flag2>|<flag3>...

输入缓冲区 querybuf

输入缓冲区存储客户端输入的指令,大小根据输入内容动态缩小扩大,最大不可超过 1G,否则导致服务器关闭该客户端。

命令与参数 (argv,argc)

客户端输入的命令会先存放到数组 argv 中,其数据结构是这样的:

当客户端输入命令后,服务器根据 argv[0] 的值再命令表中查找(命令不区分大小写)对应命令的函数并给 cmd 赋值,cmd 就是对应的命令函数相关的操作信息。

输出缓冲区(buf,bufpos,reply)

输出缓冲区有两个,一个大小固定,一个大小可变。大小固定的存储长度小的回复,比如 OK,错误返回等。大小可变缓冲区保存长度较大的回复,比如长列表,大集合。

大小可变缓冲区由 reply 链表实现,利用链表结构存储若干和字符串对象,使得长度不会受到限制。数据结构如下:

时间(ctime,lastinteraction,obuf_soft_limit_reached_time)

服务器使用两种模式来限制客户端输出缓冲区的大小:

  • 硬性限制 (hard limit): 如果输出缓冲区的大小超过了硬性限制所设置的大小,那么服务器立即关闭客户端。
  • 软性限制 (softlimit): 软性限制比硬性限制小,服务器会根据输出缓冲区大小介于软硬性限制之间的时间决定是否关闭客户端 。

客户端的创建与关闭

由于客户端有不同类型,所以创建和关闭的方式也不相同。

创建普通客户端

客户端连接时调用 connect 函数,服务器就会调用连接事件处理器,为客户端创建状态,并创建新的客户端到 client 链表末尾。

关闭普通客户端

普通客户端可因其中一个原因关闭:

  • 客户端进程退出或者被杀死
  • 客户端向服务器发送了带有不符合协议格式的命令请求
  • 客户端成为了 CLIENT KILL 命令的目标
  • 用户为服务器设置了 timeout 配置选项且当客户端的空转时间超过 timeout 时。不过 timeout 选项有客户端是主服务器,从服务器,正在被 BLPOP 等命令阻塞,正在执行 SUBSCRIBE、PSUBSCRIBE 等订阅命令,那么即使客户端的空转时间超过了 timeout 选项的值,客户端也不会被服务器关闭。
  • 客户端发送的命令请求的大小超过了输入缓冲区的限制大小 (默认为 1GB)
  • 输出缓冲区的大小超过了硬性限制所设置的大小
  • 输出缓冲区的大小超过了软性限制所设置的大小,但还没超过硬性限制的时间超过指定时间。

Lua 脚本的伪客户端

服务器初始化时创建,随服务器结束关闭。

AOF 文件的伪客户端

载入 AOF 文件时创建,载入结束关闭。