前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis使用及源码剖析-13.Redis客户端-2021-1-27

Redis使用及源码剖析-13.Redis客户端-2021-1-27

作者头像
用户7719114
发布2022-02-22 13:42:37
5470
发布2022-02-22 13:42:37
举报
文章被收录于专栏:C++小白C++小白

文章目录

前言

Redis服务端是典型的一对多程序,可以为多个客户端提供服务,Redis服务端结构体中的clients链表中保存了所有的客户端信息,如下所示:

代码语言:javascript
复制
struct redisServer {
    // 一个链表,保存了所有客户端状态结构
    list *clients;              /* List of active clients */
};

本文对Redis的客户端相关内容进行了简要介绍。

一、客户端结构体简介

1.套接字描述符

客户端的fd属性记录了客户端的套接字描述符,如下所示:

代码语言:javascript
复制
/* With multiplexing we need to take per-client state.
 * Clients are taken in a liked list.
 *
 * 因为 I/O 复用的缘故,需要为每个客户端维持一个状态。
 *
 * 多个客户端状态被服务器用链表连接起来。
 */
typedef struct redisClient {

    // 套接字描述符
    int fd;
} redisClient;

fd除了可以说客户端的已连接套接字外,还可以是-1,此时表明是一个伪客户端。伪客户端表明命令来自AOF文件或者lua脚本。

2.名字

默认情况下连接到服务器的客户端是没有名字的,可以通过client setname命令可以为客户端设置一个名字,设置的名字保存在redisclient的name成员中,如下所示:

代码语言:javascript
复制
typedef struct redisClient {
    // 客户端的名字
    robj *name;             /* As set by CLIENT SETNAME */
} redisClient;

3.标志

redisClient 的flags成员记录了客户端的角色和状态,如下所示:

代码语言:javascript
复制
typedef struct redisClient {
     // 客户端状态标志
    int flags;              /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */
} redisClient;

flags属性的可能取值如下所示:

在这里插入图片描述
在这里插入图片描述

4.输入缓冲区

redisClient中的输入缓冲区保存客户端发送的命令请求,最大不能超过1GB,如下所示:

代码语言:javascript
复制
typedef struct redisClient {
    // 查询缓冲区
    sds querybuf;
} redisClient;

5.命令和命令参数

命令保存到querybuf后,服务端会对命令进行解析,将命令参数和参数个数保存到argv成员和argc成员中,如下所示:

代码语言:javascript
复制
typedef struct redisClient {
    // 参数数量
    int argc;

    // 参数对象数组
    robj **argv;
} redisClient;

6.命令的实现函数

服务端将参数及个数放入argv和argc成员以后,argv[0]即为命令名称,服务端会根据命令表查找命令的实现函数,命令表如下所示:

代码语言:javascript
复制
/*
 * Redis 命令
 */
struct redisCommand {

    // 命令名字
    char *name;

    // 实现函数
    redisCommandProc *proc;

    // 参数个数
    int arity;

    // 字符串表示的 FLAG
    char *sflags; /* Flags as string representation, one char per flag. */

    // 实际 FLAG
    int flags;    /* The actual flags, obtained from the 'sflags' field. */

    /* Use a function to determine keys arguments in a command line.
     * Used for Redis Cluster redirect. */
    // 从命令中判断命令的键参数。在 Redis 集群转向时使用。
    redisGetKeysProc *getkeys_proc;

    /* What keys should be loaded in background when calling this command? */
    // 指定哪些参数是 key
    int firstkey; /* The first argument that's a key (0 = no keys) */
    int lastkey;  /* The last argument that's a key */
    int keystep;  /* The step between first and last key */

    // 统计信息
    // microseconds 记录了命令执行耗费的总毫微秒数
    // calls 是命令被执行的总次数
    long long microseconds, calls;
};
typedef struct redisClient {
    // 记录被客户端执行的命令
    struct redisCommand *cmd;
} redisClient;

7.输出缓冲区

命令执行完毕后,执行结果会存放在输出缓冲区中。输出缓冲区分为固定缓冲区和可变缓冲区,固定缓冲区用于保存长度较短的回复,可变缓冲区用于回复较长的回复,是一个链表对象,将回复放在链表的字符串中。缓冲区定义如下:

代码语言:javascript
复制
typedef struct redisClient {
    /* 固定缓冲区 */
    // 回复偏移量
    int bufpos;
    // 回复缓冲区
    char buf[REDIS_REPLY_CHUNK_BYTES];

    // 可变缓冲区
    list *reply;
} redisClient;

8.身份认证

redisclient的authenticated成员用于记录客户端是否通过了身份验证,如下所示:

代码语言:javascript
复制
typedef struct redisClient {
    // 当 server.requirepass 不为 NULL 时
    // 代表认证的状态
    // 0 代表未认证, 1 代表已认证
    int authenticated;      /* when requirepass is non-NULL */
} redisClient;

当服务端启动了身份认证后,如果客户端的authenticated属性是0,则除了身份认证命令auth以外,其余命令服务器均会拒绝执行。

9.时间

客户端记录了几个和时间有关的属性,如下所示:

代码语言:javascript
复制
typedef struct redisClient {
    // 创建客户端的时间
    time_t ctime;           /* Client creation time */
    // 客户端最后一次和服务器互动的时间
    time_t lastinteraction; /* time of the last interaction, used for timeout */
    // 客户端的输出缓冲区超过软性限制的时间
    time_t obuf_soft_limit_reached_time;
} redisClient;

10.完整结构体

完整的redisclient结构体定义如下:

代码语言:javascript
复制
/* With multiplexing we need to take per-client state.
 * Clients are taken in a liked list.
 *
 * 因为 I/O 复用的缘故,需要为每个客户端维持一个状态。
 *
 * 多个客户端状态被服务器用链表连接起来。
 */
typedef struct redisClient {

    // 套接字描述符
    int fd;

    // 当前正在使用的数据库
    redisDb *db;

    // 当前正在使用的数据库的 id (号码)
    int dictid;

    // 客户端的名字
    robj *name;             /* As set by CLIENT SETNAME */

    // 查询缓冲区
    sds querybuf;

    // 查询缓冲区长度峰值
    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size */

    // 参数数量
    int argc;

    // 参数对象数组
    robj **argv;

    // 记录被客户端执行的命令
    struct redisCommand *cmd, *lastcmd;

    // 请求的类型:内联命令还是多条命令
    int reqtype;

    // 剩余未读取的命令内容数量
    int multibulklen;       /* number of multi bulk arguments left to read */

    // 命令内容的长度
    long bulklen;           /* length of bulk argument in multi bulk request */

    // 回复链表
    list *reply;

    // 回复链表中对象的总大小
    unsigned long reply_bytes; /* Tot bytes of objects in reply list */

    // 已发送字节,处理 short write 用
    int sentlen;            /* Amount of bytes already sent in the current
                               buffer or object being sent. */

    // 创建客户端的时间
    time_t ctime;           /* Client creation time */

    // 客户端最后一次和服务器互动的时间
    time_t lastinteraction; /* time of the last interaction, used for timeout */

    // 客户端的输出缓冲区超过软性限制的时间
    time_t obuf_soft_limit_reached_time;

    // 客户端状态标志
    int flags;              /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */

    // 当 server.requirepass 不为 NULL 时
    // 代表认证的状态
    // 0 代表未认证, 1 代表已认证
    int authenticated;      /* when requirepass is non-NULL */

    // 复制状态
    int replstate;          /* replication state if this is a slave */
    // 用于保存主服务器传来的 RDB 文件的文件描述符
    int repldbfd;           /* replication DB file descriptor */

    // 读取主服务器传来的 RDB 文件的偏移量
    off_t repldboff;        /* replication DB file offset */
    // 主服务器传来的 RDB 文件的大小
    off_t repldbsize;       /* replication DB file size */
    
    sds replpreamble;       /* replication DB preamble. */

    // 主服务器的复制偏移量
    long long reploff;      /* replication offset if this is our master */
    // 从服务器最后一次发送 REPLCONF ACK 时的偏移量
    long long repl_ack_off; /* replication ack offset, if this is a slave */
    // 从服务器最后一次发送 REPLCONF ACK 的时间
    long long repl_ack_time;/* replication ack time, if this is a slave */
    // 主服务器的 master run ID
    // 保存在客户端,用于执行部分重同步
    char replrunid[REDIS_RUN_ID_SIZE+1]; /* master run id if this is a master */
    // 从服务器的监听端口号
    int slave_listening_port; /* As configured with: SLAVECONF listening-port */

    // 事务状态
    multiState mstate;      /* MULTI/EXEC state */

    // 阻塞类型
    int btype;              /* Type of blocking op if REDIS_BLOCKED. */
    // 阻塞状态
    blockingState bpop;     /* blocking state */

    // 最后被写入的全局复制偏移量
    long long woff;         /* Last write global replication offset. */

    // 被监视的键
    list *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS */

    // 这个字典记录了客户端所有订阅的频道
    // 键为频道名字,值为 NULL
    // 也即是,一个频道的集合
    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) */

    // 链表,包含多个 pubsubPattern 结构
    // 记录了所有订阅频道的客户端的信息
    // 新 pubsubPattern 结构总是被添加到表尾
    list *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) */
    sds peerid;             /* Cached peer ID. */

    /* Response buffer */
    // 回复偏移量
    int bufpos;
    // 回复缓冲区
    char buf[REDIS_REPLY_CHUNK_BYTES];

} redisClient;

二、客户端创建与关闭

1.普通客户端创建

当有一个新的客户端连接到服务端时,服务端就会创建一个redisclient对象,并添加到redisserver的clients链表中(见前言)。

2.普通客户端关闭

当客户端退出或者被杀死、客户端发送的命令格式错误、客户端输出缓冲区大小超限时,客户端就会被关闭,对应的redisclient对象从链表中被移除。

3.lua脚本的伪客户端

服务器在初始化时会创建负责执行lua脚本命令的伪客户端,伪客户端保存在redisserver的lua_client中,如下所示:

代码语言:javascript
复制
typedef struct redisClient {
    // 复制执行 Lua 脚本中的 Redis 命令的伪客户端
    redisClient *lua_client;   /* The "fake client" to query Redis from Lua */
} redisClient;

这个客户端在服务器运行时一直存在,服务器退出时才关闭。

4.aof文件的伪客户端

服务器在载入aof文件时,会创建执行aof命令的伪客户端,载入完成后立即关闭。

总结

本文对redis客户端结构的主要成员做了简要介绍,如有不当,请多多指正。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、客户端结构体简介
    • 1.套接字描述符
      • 2.名字
        • 3.标志
          • 4.输入缓冲区
            • 5.命令和命令参数
              • 6.命令的实现函数
                • 7.输出缓冲区
                  • 8.身份认证
                    • 9.时间
                      • 10.完整结构体
                      • 二、客户端创建与关闭
                        • 1.普通客户端创建
                          • 2.普通客户端关闭
                            • 3.lua脚本的伪客户端
                              • 4.aof文件的伪客户端
                              • 总结
                              相关产品与服务
                              云数据库 Redis
                              腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档