【Redis学习笔记】2018-06-01 set命令执行流程

顺风车运营研发团队 闫昌
一. Redis编译安装时指定参数, 防止gdb时被优化, 在make时, 增加参数noopt

 make  noopt

二. 客户端通讯协议

1.客户端与服务端的通讯协议是建立在TCP之上的
2.Redis指定了RESP(Redis SerializationProtocol, Redis序列化协议)实现客户端与服务端的正常交互
3.命令格式: *<参数数量>rn$ <参数1的字节数量>\r\n参数1\r\n$<参数2的字节数量>rn参数2rn
例如: set hello world *3rn$5\r\nset\r\n$5rnworldrn
4.返回结果
状态回复: 在RESP中第一个字节为”+”
错误回复: 在RESP中第一个字节为”-“
整数回复: 在RESP中第一个字符为”:”
字符串回复: 在RESP中第一个字符为”$”
多条字符串回复: 在RESP中第一个字节为”*”

redis-cli只能看到最终的执行结果, 因为redis-cli本身就是按照RESP进行了结果解析, 所以看不到中间结果
三. set命令执行流程: set hello world

1.redis-server启动后, 会进行server.c的main主函数里
2.当初始化服务端参数之后, 会进入aeMain函数里
3.aeMain函数里while循环调用aeProcessEvents, aeProcessEvents里调用aeApiPoll等待epoll_wait返回可用fd
4.当有客户端连接请求过来之后, 会调用networking.c的createClient函数, 此函数里调用aeCreateFileEvent, 将第四个参数指定为: readQueryFromClient
5.当客户端连接成功之后, aeApiPoll返回可用的fd后, 进入aeProcessEvents的for (j = 0; j < numevents; j++)循环, 在此例中, 进入rfileProc函数, 此时的rfileProc为上一步的readQueryFromClient方法
6.readQueryFromClient函数, —- networking.c

void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    client *c = (client*) privdata;
    int nread, readlen;
    size_t qblen;
      ...
    readlen = PROTO_IOBUF_LEN;//16K
    ...
    ...
    qblen = sdslen(c->querybuf);//此时的值为0
    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);//给querybuf分配16K的空间
    nread = read(fd, c->querybuf+qblen, readlen);//从fd中读取16K的内容, 此时的fd为客户端向服务端的socket套接字
    if (nread == -1) {
        if (errno == EAGAIN) {//errno是全局变量, 在read函数后生成的变量, 此变量用一次之后就会消失,  此时EAGAIN表示读取失败
            return;
        } else {
            serverLog(LL_VERBOSE, "Reading from client: %s",strerror(errno));
            freeClient(c);
            return;
        }
    } else if (nread == 0) {
        serverLog(LL_VERBOSE, "Client closed connection");
        freeClient(c);
        return;
    } else if (c->flags & CLIENT_MASTER) {
        c->pending_querybuf = sdscatlen(c->pending_querybuf,
                                        c->querybuf+qblen,nread);
    }

    sdsIncrLen(c->querybuf,nread);//扩展querybuf的长度, 使其长度为读取出的命令的长度
    c->lastinteraction = server.unixtime;
      ....
    ....
    if (!(c->flags & CLIENT_MASTER)) {
        processInputBuffer(c);//处理命令
    } else {
        size_t prev_offset = c->reploff;
        processInputBuffer(c);
        size_t applied = c->reploff - prev_offset;
        if (applied) {
            replicationFeedSlavesFromMasterStream(server.slaves,
                    c->pending_querybuf, applied);
            sdsrange(c->pending_querybuf,applied,-1);
        }
    }
}
  1. processInputBuffer函数: —- networking.c
void processInputBuffer(client *c) {
    server.current_client = c;
    while(sdslen(c->querybuf)) {
       ......
       ......
        if (c->argc == 0) {
            resetClient(c);
        } else {
            if (processCommand(c) == C_OK) {//这里进入执行命令函数时
                if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {
                    c->reploff = c->read_reploff - sdslen(c->querybuf);
                }

                if (!(c->flags & CLIENT_BLOCKED) || c->btype != BLOCKED_MODULE)
                    resetClient(c);
            }

            if (server.current_client == NULL) break;
        }
    }
    server.current_client = NULL;
}

8.processCommand函数(server.c)的最后调用了call(server.c)方法

9.call方法里调用了c->cmd->proc方法, 而这个方法就是t_string.c的setCommand方法

c->cmd->proc(c);

10.setCommand方法(t_string.c)里调用了setGenericCommand方法
11.setGenericCommand方法调用了setKey方法
12.setKey方法调用了dbAdd或dbOverwrite方法, 将key和val设置到redis数据库

四. 执行流程

《【Redis学习笔记】2018-06-01 set命令执行流程》

    原文作者:LNMPR源码研究
    原文地址: https://segmentfault.com/a/1190000015334548
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞