Redis源码解析-通信协议

Redis 通信协议

注解:本文的内容参考了硬核课堂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"));

下面待补充

你可能感兴趣的:(Redis,redis,数据库,nosql)