redis7源码分析:redis 单线程模型解析,一条get命令执行流程

redis7,源码,分析,redis,单线程,模型,解析,一条,get,命令,执行,流程 · 浏览次数 : 4

小编点评

**梳理后redis 启动流程再来解析并处理客户端发来的命令** 1. **当 client fd 可读时,会回调readQueryFromClient函数** - 读取客户端发来的命令并进行解析。 - 如果命令成功执行,更新客户端内存的输出缓存大小。 2. **分析 processInputBuffer** - 读取客户端输入的字节,并处理并检查命令格式、参数、阻塞等信息。 - 如果命令成功执行,更新客户端内存的输出缓存大小。 3. **分析 processCommandAndResetClient** - 进程命令并重置客户端状态。 - 如果命令执行成功,更新客户端内存的输出缓存大小。 4. **分析 processCommand** - 搜索命令并执行命令。 - 如果命令成功执行,更新客户端内存的输出缓存大小。 5. **调用 cmd 对应的proc** - 获取命令对象。 - 执行命令。 6. **最终调用 cmd 对应的proc** - 处理 cmd 对应的 proc,例如处理 get 请求。

正文

有了下文的梳理后

redis 启动流程

再来解析redis 在单线程模式下解析并处理客户端发来的命令

1. 当 client fd 可读时,会回调readQueryFromClient函数

void readQueryFromClient(connection *conn) {
client *c = connGetPrivateData(conn);
    int nread, big_arg = 0;
    size_t qblen, readlen;

    /* Check if we want to read from the client later when exiting from
     * the event loop. This is the case if threaded I/O is enabled. */
    // 这里是多线程模型走的地方,被文分析单线程模式,不走这里
    if (postponeClientRead(c)) return;
    ......
    /* There is more data in the client input buffer, continue parsing it
     * and check if there is a full command to execute. */
    //  走到这里,开始处理客户端发来的命令
    if (processInputBuffer(c) == C_ERR)
         c = NULL;
}

2. 分析processInputBuffer

int processInputBuffer(client *c) {
    /* Keep processing while there is something in the input buffer */
    while(c->qb_pos < sdslen(c->querybuf)) {
        if (c->flags & CLIENT_BLOCKED) break;
        if (c->flags & CLIENT_PENDING_COMMAND) break;
        if (isInsideYieldingLongCommand() && c->flags & CLIENT_MASTER) break;

        if (c->flags & (CLIENT_CLOSE_AFTER_REPLY|CLIENT_CLOSE_ASAP)) break;

        ......

        /* Multibulk processing could see a <= 0 length. */
        if (c->argc == 0) {
            resetClient(c);
        } else {
            if (io_threads_op != IO_THREADS_OP_IDLE) {
                serverAssert(io_threads_op == IO_THREADS_OP_READ);
                c->flags |= CLIENT_PENDING_COMMAND;
                break;
            }

            /* We are finally ready to execute the command. */
            // 最终在这里执行命令
            if (processCommandAndResetClient(c) == C_ERR) {
                return C_ERR;
            }
        }
    }
    ......

    return C_OK;
}

2.1 分析 processCommandAndResetClient

int processCommandAndResetClient(client *c) {
    int deadclient = 0;
    client *old_client = server.current_client;
    server.current_client = c;
    // 这里处理命令
    if (processCommand(c) == C_OK) {
        commandProcessed(c);
        /* Update the client's memory to include output buffer growth following the
         * processed command. */
        updateClientMemUsageAndBucket(c);
    }

    if (server.current_client == NULL) deadclient = 1;
    /*
     * Restore the old client, this is needed because when a script
     * times out, we will get into this code from processEventsWhileBlocked.
     * Which will cause to set the server.current_client. If not restored
     * we will return 1 to our caller which will falsely indicate the client
     * is dead and will stop reading from its buffer.
     */
    server.current_client = old_client;
    /* performEvictions may flush slave output buffers. This may
     * result in a slave, that may be the active client, to be
     * freed. */
    return deadclient ? C_ERR : C_OK;
}

2.2 分析 processCommand

int processCommand(client *c) {
    ......
    /* Now lookup the command and check ASAP about trivial error conditions
     * such as wrong arity, bad command name and so forth. */
    // 这里搜索命令,sentinel的命令也是在这里处理
    // 不像老版本,6.0以前,使用的是命令覆盖的方式,改用搜索
    c->cmd = c->lastcmd = c->realcmd = lookupCommand(c->argv,c->argc);

    ......
    /* Exec the command */
    if (c->flags & CLIENT_MULTI &&
        c->cmd->proc != execCommand &&
        c->cmd->proc != discardCommand &&
        c->cmd->proc != multiCommand &&
        c->cmd->proc != watchCommand &&
        c->cmd->proc != quitCommand &&
        c->cmd->proc != resetCommand)
    {
        queueMultiCommand(c, cmd_flags);
        addReply(c,shared.queued);
    } else {
        //最终普通命令调用 call函数执行
        call(c,CMD_CALL_FULL);
        c->woff = server.master_repl_offset;
        if (listLength(server.ready_keys) && !isInsideYieldingLongCommand())
            handleClientsBlockedOnKeys();
    }
}

2.3 call函数在server.c中

void call(client *c, int flags) {
......
    server.in_nested_call++;
    c->cmd->proc(c);
    server.in_nested_call--;
......
}

最终调用cmd 对应的proc

假设是get 请求,那么在lookupCommand时就会找到对应的command 对象,由redisCommandTable可知,对应方法为getCommand

t_string.c

void getCommand(client *c) {
    getGenericCommand(c);
}

int getGenericCommand(client *c) {
    robj *o;

    // 搜索db中数据
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL)
        return C_OK;

    if (checkType(c,o,OBJ_STRING)) {
        return C_ERR;
    }

    // 将数据写入到输出缓存中,待eventloop 发现可写后发送
    addReplyBulk(c,o);
    return C_OK;
}

与redis7源码分析:redis 单线程模型解析,一条get命令执行流程相似的内容:

redis7源码分析:redis 单线程模型解析,一条get命令执行流程

有了下文的梳理后 redis 启动流程 再来解析redis 在单线程模式下解析并处理客户端发来的命令 1. 当 client fd 可读时,会回调readQueryFromClient函数 void readQueryFromClient(connection *conn) { client *c

redis7源码分析:redis 启动流程

1. redis 由 server.c 的main函数启动 int main(int argc, char **argv) { ... // 上面的部分为读取配置和启动命令参数解析,看到这一行下面为启动流程 serverLog(LL_WARNING, "oO0OoO0OoO0Oo Redis is

redis7源码分析:redis 多线程模型解析

多线程模式中,在main函数中会执行InitServerLast void InitServerLast() { bioInit(); // 关键一步, 这里启动了多条线程,用于执行命令,redis起名为IO 线程 initThreadedIO(); set_jemalloc_bg_thread(s

redis 源码分析:Jedis 哨兵模式连接原理

1. 可以从单元测试开始入手 查看类JedisSentinelPool private static final String MASTER_NAME = "mymaster"; protected static final HostAndPort sentinel1 = HostAndPorts.

[转帖]Redis 4.0 自动内存碎片整理(Active Defrag)源码分析

阅读本文前建议先阅读此篇博客: Redis源码从哪里读起 Redis 4.0 版本增加了许多不错的新功能,其中自动内存碎片整理功能 activedefrag 肯定是非常诱人的一个,这让 Redis 集群回收内存碎片相比 Redis 3.0 更加优雅,便利。我们升级 Redis 4.0 后直接开启了a

Redisson 限流器源码分析

Redisson 限流器源码分析 对上篇文章网友评论给出问题进行解答:redis 的key 是否会过期,过期指的限流器 可以先阅读上篇文章: redis + AOP + 自定义注解实现接口限流 - 古渡蓝按 - 博客园 (cnblogs.com) 注解AOP 代码部分提取 // 调用Reids工具类

Redisson 限流器源码分析

Redisson 限流器源码分析 对上篇文章网友评论给出问题进行解答:redis 的key 是否会过期 可以先阅读上篇文章: redis + AOP + 自定义注解实现接口限流 - 古渡蓝按 - 博客园 (cnblogs.com) 注解AOP 代码部分提取 // 调用Reids工具类的rateLim

跳跃表数据结构与算法分析

目前市面上充斥着大量关于跳跃表结构与Redis的源码解析,但是经过长期观察后发现大都只是在停留在代码的表面,而没有系统性地介绍跳跃表的由来以及各种常量的由来。作为一种概率数据结构,理解各种常量的由来可以更好地进行变化并应用到高性能功能开发中。本文没有重复地以对现有优秀实现进行代码分析,而是通过对跳跃表进行了系统性地介绍与形式化分析,并给出了在特定场景下的跳跃表扩展方式,方便读者更好地理解跳跃表数据

记一次Redis Cluster Pipeline导致的死锁问题

本文介绍了一次排查Dubbo线程池耗尽问题的过程。通过查看Dubbo线程状态、分析Jedis连接池获取连接的源码、排查死锁条件等方面,最终确认是因为使用了cluster pipeline模式且没有设置超时时间导致死锁问题。

[转帖]Redis进阶实践之十三 Redis的Redis-trib.rb脚本文件使用详解

https://www.cnblogs.com/PatrickLiu/p/8484784.html 一、简介 事先说明一下,本篇文章不涉及对redis-trib.rb源代码的分析,只是从使用的角度来阐述一下,对第一次使用的人来说很重要。redis-trib.rb是redis官方推出的管理redis集