redis源码阅读(3)-解析客户端请求

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hehe123456ZXC/article/details/53222486

客户端连接server端之后,server端会注册client 读事件,以检测客户端的 写操作,回调函数是:
readQueryFromClient函数用来读取客户端的请求命令行数据,并调用processInputBuffer函数依照redis通讯协议对数据进行解析。服务器使用最原始的read函数来读取客户端发送来的请求命令,并将字符串存储在querybuf中,根据需要对querybuf进行扩展。

void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask)
{
    //el: 事件循环结构体
    //fd:读事件发生的client的描述符
    //privadata: server端保存的client信息
    //mask:事件类型
    //转换client结构体
    redisClient *c = (redisClient*) privdata;
    int nread, readlen;
    size_t qblen;
    REDIS_NOTUSED(el);
    REDIS_NOTUSED(mask);

    server.current_client = c;
    readlen = REDIS_IOBUF_LEN;
    /* If this is a multi bulk request, and we are processing a bulk reply
     * that is large enough, try to maximize the probability that the query
     * buffer contains exactly the SDS string representing the object, even
     * at the risk of requiring more read(2) calls. This way the function
     * processMultiBulkBuffer() can avoid copying buffers to create the
     * Redis Object representing the argument. */
    if (c->reqtype == REDIS_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1
        && c->bulklen >= REDIS_MBULK_BIG_ARG)
    {
        int remaining = (int)((unsigned)(c->bulklen+2)-sdslen(c->querybuf));

        if (remaining < readlen) readlen = remaining;
    }
    //获取querybuf的长度
    qblen = sdslen(c->querybuf);
    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;
    //对querybuf的空间进行扩展 ,如果sds的free长度小于readlen 则拓展其长度,并并进行旧数据的赋值拷贝操作,最后返回新的内存其实地址
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
    //调用原始的read函数读取socket中buffer数据
    nread = read(fd, c->querybuf+qblen, readlen);
        if (nread == -1) {
        if (errno == EAGAIN) {
            nread = 0;
        } else {
#ifdef _WIN32
            redisLog(REDIS_VERBOSE, "Reading from client: %s",wsa_strerror(errno));
#else
            redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));
#endif
            freeClient(c);
            return;
        }//如果返回的长度为0 则表示客户端已经关闭了连接,调用freeClient关闭连接
    } else if (nread == 0) {
        redisLog(REDIS_VERBOSE, "Client closed connection");
        freeClient(c);
        return;
    }
    //更新此客户端的sds的长度
   if (nread) {
        sdsIncrLen(c->querybuf,nread);
        c->lastinteraction = server.unixtime;
    } else {
        server.current_client = NULL;
        return;
    } 
    //如果此客户端的sds长度超过了最大长度,那么直接关闭client
        if (sdslen(c->querybuf) > server.client_max_querybuf_len) {
        sds ci = getClientInfoString(c), bytes = sdsempty();

        bytes = sdscatrepr(bytes,c->querybuf,64);
        redisLog(REDIS_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes);
        sdsfree(ci);
        sdsfree(bytes);
        freeClient(c);
        return;
    }
    //调用processInputBuffer处理数据
    processInputBuffer(c);
    server.current_client = NULL;
}
void processInputBuffer(redisClient *c) {
    /* Keep processing while there is something in the input buffer */
    while(sdslen(c->querybuf)) {
        /* Immediately abort if the client is in the middle of something. */
        if (c->flags & REDIS_BLOCKED) return;

        /* REDIS_CLOSE_AFTER_REPLY closes the connection once the reply is
         * written to the client. Make sure to not let the reply grow after
         * this flag has been set (i.e. don't process more commands). */
        if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
    //如果不知道client的请求类型,判断client的请求类型
    //REDIS_REQ_MULTIBULK 多包请求。比如说(key,set())读取key的全部元素
    //REDIS_REQ_INLINE 单个请求
        /* Determine request type when unknown. */
        if (!c->reqtype) {
            if (c->querybuf[0] == '*') {
                c->reqtype = REDIS_REQ_MULTIBULK;
            } else {
                c->reqtype = REDIS_REQ_INLINE;
            }
        }
    //根据不同的请求类型,调用不同处理函数
        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;
        } else {
            redisPanic("Unknown request type");
        }

        /* Multibulk processing could see a <= 0 length. */
        if (c->argc == 0) {
            resetClient(c);
        } else {
            /* Only reset the client when the command was executed. */
            // 响应client端的命令,函数注释见下面
            if (processCommand(c) == REDIS_OK)
                resetClient(c);
        }
    }
}
int processMultibulkBuffer(redisClient *c) {
    char *newline = NULL;
    int pos = 0, ok;
    long long ll;

    if (c->multibulklen == 0) {
        /* The client should have been reset */
        redisAssertWithInfo(c,NULL,c->argc == 0);

        /* Multi bulk length cannot be read without a \r\n */
        newline = strchr(c->querybuf,'\r');
        if (newline == NULL) {
            if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {
                addReplyError(c,"Protocol error: too big mbulk count string");
                setProtocolError(c,0);
            }
            return REDIS_ERR;
        }

        /* Buffer should also contain \n */
        if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
            return REDIS_ERR;

        /* We know for sure there is a whole line since newline != NULL,
         * so go ahead and find out the multi bulk length. */
        redisAssertWithInfo(c,NULL,c->querybuf[0] == '*');
        //获取包的个数,如果个数正常,那么返回1,否则返回error,并将包的个数赋予ll

        ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);
        if (!ok || ll > 1024*1024) {
            addReplyError(c,"Protocol error: invalid multibulk length");
            setProtocolError(c,pos);
            return REDIS_ERR;
        }

        pos = (int)(newline-c->querybuf)+2;
        if (ll <= 0) {
            c->querybuf = sdsrange(c->querybuf,pos,-1);
            return REDIS_OK;
        }
        //包的个数
        c->multibulklen = (int)ll;

        /* Setup argv array on client structure */
        if (c->argv) zfree(c->argv);
        //根据包的个数,开辟空间,赋予c->argv
        c->argv = zmalloc(sizeof(robj*)*c->multibulklen);
    }
    //循环读取每个包的长度,以及包的内容,将其存到argc argv 相互对应
    while(c->multibulklen) {
        /* Read bulk length if unknown */
        //获取每个报的字节长度
        if (c->bulklen == -1) {
            newline = strchr(c->querybuf+pos,'\r');
            if (newline == NULL) {
                if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {
                    addReplyError(c,"Protocol error: too big bulk count string");
                    setProtocolError(c,0);
                }
                break;
            }

            /* Buffer should also contain \n */
            if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
                break;

            if (c->querybuf[pos] != '$') {
                addReplyErrorFormat(c,
                    "Protocol error: expected '$', got '%c'",
                    c->querybuf[pos]);
                setProtocolError(c,pos);
                return REDIS_ERR;
            }

            ok = string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll);
            if (!ok || ll < 0 || ll > 512*1024*1024) {
                addReplyError(c,"Protocol error: invalid bulk length");
                setProtocolError(c,pos);
                return REDIS_ERR;
            }

            pos += (int)(newline-(c->querybuf+pos)+2);
            if (ll >= REDIS_MBULK_BIG_ARG) {
                /* If we are going to read a large object from network
                 * try to make it likely that it will start at c->querybuf
                 * boundary so that we can optimized object creation
                 * avoiding a large copy of data. */
                c->querybuf = sdsrange(c->querybuf,pos,-1);
                pos = 0;
                /* Hint the sds library about the amount of bytes this string is
                 * going to contain. */
                c->querybuf = sdsMakeRoomFor(c->querybuf,(size_t)(ll+2));
            }
            c->bulklen = (long)ll;
        }

        /* Read bulk argument */
        //读取包参数,将其内存首地址赋值到c->argv[argc]
        if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) {
            /* Not enough data (+2 == trailing \r\n) */
            break;
        } else {
            /* Optimization: if the buffer contains JUST our bulk element
             * instead of creating a new object by *copying* the sds we
             * just use the current sds string. */
            if (pos == 0 &&
                c->bulklen >= REDIS_MBULK_BIG_ARG &&
                (signed) sdslen(c->querybuf) == c->bulklen+2)
            {
                c->argv[c->argc++] = createObject(REDIS_STRING,c->querybuf);
                sdsIncrLen(c->querybuf,-2); /* remove CRLF */
                c->querybuf = sdsempty();
                /* Assume that if we saw a fat argument we'll see another one
                 * likely... */
                c->querybuf = sdsMakeRoomFor(c->querybuf,c->bulklen+2);
                pos = 0;
            } else {
                c->argv[c->argc++] =
                    createStringObject(c->querybuf+pos,c->bulklen);
                pos += c->bulklen+2;
            }
            c->bulklen = -1;
            c->multibulklen--;
        }
    }
        /* Trim to pos */
    //将已经读取到的数据删除,将剩余的数据赋值到前面,即更新c->querybuf
    if (pos) c->querybuf = sdsrange(c->querybuf,pos,-1);

    /* We're done when c->multibulk == 0 */
    if (c->multibulklen == 0) return REDIS_OK;

    /* Still not read to process the command */
    return REDIS_ERR;
}

int processInlineBuffer(redisClient *c) 解析单个包的的请求,情况和上面多个包的情况类似

函数执行到此,server已经知晓client的具体的命令,以及命令的参数,命令以及参数存储在client->argc 和client->argv中,下面就是具体的处理请求了,请求的函数是
int processCommand(redisClient *c),见下一小节

猜你喜欢

转载自blog.csdn.net/hehe123456ZXC/article/details/53222486