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;
}