redis6.0.5之客户端发送命令参数

Clients send commands to a Redis server as a RESP Array of Bulk Strings.
客户端发送命令到redis服务采用了Bulk Strings的RESP格式数组
For Arrays the first byte of the reply is "*"
对RESP格式数组,第一个回复的字符是*
RESP Arrays are sent using the following format:
RESP格式的数组使用如下格式发送(给服务端)
A * character as the first byte, followed by the number of elements in the array as a decimal number, followed by CRLF.
一个*字符当作第一个字节,紧跟着是一个表示数组中元素的个数的10进制数,以\r\n结尾
An additional RESP type for every element of the Array.
外加数组中每个元素的RESP类型
So an empty Array is just the following:
所以一个空数组如下所示
"*0\r\n"
While an array of two RESP Bulk Strings "foo" and "bar" is encoded as:
一个具有两个RESP类型的 Bulk Strings 数组 "foo""bar"  编码如下所示:
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"
Bulk Strings are used in order to represent a single binary safe string up to 512 MB in length.
Bulk Strings用来表示一个最大值不超过512MB的二进制安全字符串
Bulk Strings are encoded in the following way:
Bulk Strings采用如下方式编码:
A "$" byte followed by the number of bytes composing the string (a prefixed length), terminated by CRLF.
一个$开头,紧跟着组成这个字符串的字节个数,以CRLF结尾
The actual string data.
实际的字符串
A final CRLF.
一个结尾符CRLF

***********************************************************************
函数countDigits返回数值的数字个数
/* Return the number of digits of 'v' when converted to string in radix 10.
 * Implementation borrowed from link in redis/src/util.c:string2ll(). */
当V转成10进制字符串时候返回的数字个数。实现从redis/src/util.c:string2ll()借鉴而来
static uint32_t countDigits(uint64_t v) {
  uint32_t result = 1;
  for (;;) {
    if (v < 10) return result;                 一个数字
    if (v < 100) return result + 1;          二个数字
    if (v < 1000) return result + 2;        三个数字
    if (v < 10000) return result + 3;          四个数字
    v /= 10000U;
    result += 4; 四个四个数数字
  }
}
***********************************************************************
/* Helper that calculates the bulk length given a certain string length. */
对于一个给定字符串计算redis格式化所需长度
static size_t bulklen(size_t len) {
    return 1+countDigits(len)+2+len+2;
    1个标志字节+长度的数值的数字个数+"/r/n"表示的两个字节+长度的数值+"/r/n"表示的两个字节
}
***********************************************************************
/* Format a command according to the Redis protocol using an sds string and
 * sdscatfmt for the processing of arguments. This function takes the
 * number of arguments, an array with arguments and an array with their
 * lengths. If the latter is set to NULL, strlen will be used to compute the
 * argument lengths.
 */
按照redis的协议格式化命令,使用sds字符串和sdscatfmt处理参数,
这个函数需要参数的个数,一个参数的数组和一个参数长度的数组。
如果后者(参数长度的数组)设置为null,那么函数strlen将被用于参数长度的计算
int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
                              const size_t *argvlen)
{
    sds cmd;
    unsigned long long totlen;
    int j;
    size_t len;

    /* Abort on a NULL target */
    if (target == NULL)
        return -1;

    /* Calculate our total size */
    totlen = 1+countDigits(argc)+2; //头部
            一个标志符(*)+长度的数值的数字个数+"/r/n"表示的两个字节
    for (j = 0; j < argc; j++) {  //每个参数
        len = argvlen ? argvlen[j] : strlen(argv[j]); //有参数长度数组则用,没有就用函数strlen计算
        totlen += bulklen(len);
    }

    /* Use an SDS string for command construction */
    cmd = sdsempty();
    if (cmd == NULL)  申请空sds字符串,如果没有内存空间也会报错
        return -1;

    /* We already know how much storage we need */ 
    cmd = sdsMakeRoomFor(cmd, totlen); //给新的sds字符串申请totlen的空间
    if (cmd == NULL)
        return -1;

    /* Construct command */
    cmd = sdscatfmt(cmd, "*%i\r\n", argc);  //格式化头部
    for (j=0; j < argc; j++) {
        len = argvlen ? argvlen[j] : strlen(argv[j]);
        cmd = sdscatfmt(cmd, "$%u\r\n", len); //格式化参数长度
        cmd = sdscatlen(cmd, argv[j], len);  //拼接sds字符串
        cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); 
        //拼接结尾的\r\n,这里需要注意sizeof("\r\n")=3,所以需要减去1
    }

    assert(sdslen(cmd)==totlen); //确认长度是否正常

    *target = cmd;
    return totlen;
}

 

posted on 2020-11-18 21:02  子虚乌有  阅读(160)  评论(0)    收藏  举报