注解:本文的内容参考了硬核课堂Redis源码解析,感兴趣的同学可移步b站
Redis 内置的通信协议叫做 RESP(Redis Serialization Protocol), 规约了一些通信的格式,没有约定就无法准确的收发数据嘛。
发送数据
因为我们是用 tcp 协议来进行通信的嘛, tcp 的其中一个特点就是 基于字节流。
那么我们为了解决 粘包 现象,就像 HTTP 那样是使用了 CRLF 作为分隔符号。
那么下面附上源码,一眼明了.
// 函数传入的参数包括 argc,argv等
// 那么最终就能够通过传入的参数构建 格式化好的 cmd 命令了
/* We already know how much storage we need */
cmd = sdsMakeRoomFor(cmd, 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]); // 每个参数都是 $\r\n\r\n
cmd = sdscatfmt(cmd, "$%u\r\n", len);
cmd = sdscatlen(cmd, argv[j], len);
cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
}
客户端格式化
其实回送给客户端的当然是将一些格式啊啥的处理掉的,然后只返回比较清晰易懂的那些数据
// Redis Reply 类型
/* This is the reply object returned by redisCommand() */
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
double dval; /* The double when type is REDIS_REPLY_DOUBLE */
size_t len; /* Length of string */
char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
and REDIS_REPLY_DOUBLE (in additionl to dval). */
char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
terminated 3 character content type, such as "txt". */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;
// Redis 的部分处理逻辑
static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
sds out = sdsempty();
switch (r->type) {
case REDIS_REPLY_ERROR:
out = sdscatprintf(out,"(error) %s\n", r->str);
break;
case REDIS_REPLY_STATUS:
out = sdscat(out,r->str);
out = sdscat(out,"\n");
break;
case REDIS_REPLY_INTEGER:
out = sdscatprintf(out,"(integer) %lld\n",r->integer);
break;
case REDIS_REPLY_DOUBLE:
out = sdscatprintf(out,"(double) %s\n",r->str);
break;
case REDIS_REPLY_STRING:
case REDIS_REPLY_VERB:
/* If you are producing output for the standard output we want
* a more interesting output with quoted characters and so forth,
* unless it's a verbatim string type. */
if (r->type == REDIS_REPLY_STRING) {
out = sdscatrepr(out,r->str,r->len);
out = sdscat(out,"\n");
} else {
out = sdscatlen(out,r->str,r->len);
out = sdscat(out,"\n");
}
break;
xxx
还是比较简单的哈
Redis常用数据的优化
比如说像
Err
,OK
啦这样的字符串我们是经常用到的,所以说我们可以直接进行分配好就行了。例如说声明为 static 类型之类的,使用的时候直接调用就好了。像 Redis 就进行了这么一个优化
server.h
extern struct sharedObjectsStruct shared; // 外部文件直接进行内部数据的填充
server.c
// 直接补充好,想用的时候直接调用 shared 就行了
void createSharedObjects(void) {
int j;
shared.crlf = createObject(OBJ_STRING,sdsnew("\r\n"));
shared.ok = createObject(OBJ_STRING,sdsnew("+OK\r\n"));
shared.err = createObject(OBJ_STRING,sdsnew("-ERR\r\n"));
shared.emptybulk = createObject(OBJ_STRING,sdsnew("$0\r\n\r\n"));
shared.czero = createObject(OBJ_STRING,sdsnew(":0\r\n"));
shared.cone = createObject(OBJ_STRING,sdsnew(":1\r\n"));
shared.emptyarray = createObject(OBJ_STRING,sdsnew("*0\r\n"));
shared.pong = createObject(OBJ_STRING,sdsnew("+PONG\r\n"));
shared.queued = createObject(OBJ_STRING,sdsnew("+QUEUED\r\n"));
shared.emptyscan = createObject(OBJ_STRING,sdsnew("*2\r\n$1\r\n0\r\n*0\r\n"));
shared.wrongtypeerr = createObject(OBJ_STRING,sdsnew(
"-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"));
shared.nokeyerr = createObject(OBJ_STRING,sdsnew(
"-ERR no such key\r\n"));
shared.syntaxerr = createObject(OBJ_STRING,sdsnew(
"-ERR syntax error\r\n"));
下面待补充