Redis 源码阅读&实践-GET命令背后的源码逻辑

本文深入探讨Redis 3.0版本GET命令的源码逻辑,从服务器启动过程回顾到GET命令的处理流程,包括参数处理、命令查找、字典查找等步骤。文章通过源码分析,揭示了Redis如何处理客户端的GET请求并返回结果。
摘要由CSDN通过智能技术生成

大家好,我是弟弟!最近读了一遍 黄健宏大佬的 <<Redis 设计与实现>>,对Redis 3.0版本有了一些认识,该书作者有一个添加了注释的 redis 3.0版本源码。

网上说Redis代码写得很好,为了加深印象和学习redis大佬的代码写作艺术,了解工作中使用的redis 命令背后的源码逻辑,便有了从redis命令角度学习redis源码的想法。
(全文提到的redis服务器,都指在 mac os 上启动的一个默认配置的单机redis服务器)

Redis服务器启动过程回顾

在上一篇博客中了解到,redis服务器是一个事件驱动的单线程服务器,
比如:客户端的链接请求是一个文件的读事件,除了文件事件意外,还有时间事件。

其中redis大佬对 事件 进行了抽象, 不管事件底层选取的是select, epoll, kqueue
都抽象出如下信息

  1. 结构体 aeApiState (在 kqueue.c 里包含一个 i/o对象kqfd,以及一个事件数组)
  2. 函数 aeApiCreate (在kqueue.c里, 调用 kqueue()获取fd, 并为事件数组分配空间)
  3. 函数 aeApiAddEvent (在kqueue.c里, 给kqfd设需要监听的fd,监听的事件类型(读/写))
  4. 函数 aeApiPoll (在kqueue.c里, 通过kqfd获取活跃事件并存放到 事件数组里)
  5. 函数 aeApiDelEvent (在kqueue.c里, 删除kqfd设需要监听的fd,监听的事件类型(读/写))
  6. 函数 aeApiResize (在kqueue.c里, 调整事件数组大小)
  7. 函数 aeApiFree (c语言,手动释放aeApiState对象)
  8. 函数 aeApiName (选取的底层i/o模型名字)

对事件进行抽象的好处就在于,调用方可以不用关心底层实现,按抽象出来的函数写一套代码就可以了,不用ctrl c+v产生大量冗余代码。没有大量冗余代码,也使得代码逻辑清晰明了。🐂🍺

对各种事件的处理函数,是放在了全局的redisServer对象里,通过活跃事件的fd、读/写事件类型 在redisServer对象中 关联了对应的事件处理函数
上一篇博客传送门

GET命令背后的源码逻辑

GET 使用场景

在我们愉快的发送 GET命令前,不禁想要问自己,在工作中什么情况下会用到这个 GET 命令,如果哪儿都用不到的话,我是不是可以不用学了!😃
当然一个很容易能想到的场景就是,缓存 key: id, value: id_info 这种数据的时候,再具体一点,就是通过uid查用户信息。
好了,服务器搞起来了,客户端也连上了,连发送命令的理由都想好了,那就来让我们愉快的发送GET命令吧!

GET命令背后的源码逻辑

请求命令的参数处理

SET命令比GET命令稍微复杂了一点点,我们先SET一个值来GET它看看。

  1. SET uid.1 我是uid1的用户信息 SET命令 发送!
  2. GET uid.1 GET命令发送!
    从下面的截图我们可以看到, set命令成功
    但是get命令打错了哈,不好意思。😅
    观众: “没关系,原谅你再打一次”
    在我们重打一次GET命令前,我们看到redisClient上提示 (error) ERR unknown command 'ge’
    好学的我不禁思考起来,redis应该是有一个支持的命令列表吧,它才知道我们打错了命令。
    观众:“emm,(这)(不)(是)(废)(话吗)”
    在这里插入图片描述
    那我们来简单看一下redis服务器收到客户端的GET命令后是一个什么样的处理流程
    从上一篇我们知道处理客户端命令的函数是readQueryFromClient, 下面是缩水的源码。
    可以看到的操作是, 从fd中读取请求数据到 redisClient->querybuf中,
    这个querybuf是一个sds对象,不慌下面会说这个sds是啥
    然后调用processInputBuffer函数进行处理,那我们接着往下看
/*
 * 读取客户端的查询缓冲区内容
 */
 //... 代表省略x行代码
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask)
{
   
    redisClient *c = (redisClient *)privdata;
    int nread, readlen;
    size_t qblen;
    ...
    // 读入长度(默认为 16 MB)
    readlen = REDIS_IOBUF_LEN;
	...
    qblen = sdslen(c->querybuf);
    ...
    // 为查询缓冲区分配空间
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
    // 读入内容到查询缓存
    nread = read(fd, c->querybuf + qblen, readlen);
	...
    // 从查询缓存重读取内容,创建参数,并执行命令
    // 函数会执行到缓存中的所有内容都被处理完为止
    processInputBuffer(c);
	...
}

从下面这个while循环里,我们凭感觉来感觉一下,
c->querybuf 可能存了1条以上的客户端发来的命令,然后呢,每次处理一条命令
这两个函数 processInlineBuffer,processMultibulkBuffer 做的事情就是从querybuf中提取出一条命令的各个参数并放到 c->argv参数数组里,c->argc里放参数个数
从名字上来看 processCommand 应该是实际执行命令的函数,那我们接着往下看


// 处理客户端输入的命令内容
void processInputBuffer(redisClient *c)
{
   
    while (sdslen(c->querybuf))
    {
   
		...
        if (c->reqtype == REDIS_REQ_INLINE)
        {
   
            if (processInlineBuffer(c) != REDIS_OK)
                break;
        }
        else if (c->reqtype == REDIS_REQ_MULTIBULK)
        {
   
            if (processMultibulkBuffer(c) != REDIS_OK)
                break;
        }
		...
        if (c->argc != 0)
        {
   
            // 执行命令,并重置客户端
            if (processCommand(c) == REDIS_OK)
                resetClient(c);
        }
        ...
    }
}

redis命令列表

从下图可以看出,processCommand正如其命,只是处理命令而已,
可以看到一个 c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
c->argv[0]->ptr 就是我们发送的命令里,按空格分隔的第一个字符串,也就是我们发送的 get
👇下面这一顿操作,就是在查找命令

int processCommand(redisClient *c)
{
   
	...
	/* Now lookup the command and check ASAP about trivial error conditions
     * such as wrong arity, bad command name and so forth. */
    // 查找命令,并进行命令合法性检查,以及命令参数个数检查
    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
    if (!c->cmd)
    {
   
        // 没找到指定的命令
        flagTransaction(c);
        addReplyErrorFormat(c, "unknown command '%s'",
                            (char *)c-
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值