• 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);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    客户端格式化

    其实回送给客户端的当然是将一些格式啊啥的处理掉的,然后只返回比较清晰易懂的那些数据

    // 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    还是比较简单的哈

    Redis常用数据的优化

    比如说像 Err, OK 啦这样的字符串我们是经常用到的,所以说我们可以直接进行分配好就行了。例如说声明为 static 类型之类的,使用的时候直接调用就好了。

    Redis 就进行了这么一个优化

    server.h
      
    extern struct sharedObjectsStruct shared;			// 外部文件直接进行内部数据的填充
    
    • 1
    • 2
    • 3
    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"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    下面待补充

  • 相关阅读:
    音视频开发常见问题(四):视频花屏和绿屏
    web前端电影项目作业源码 大学生影视主题网页制作电影网页设计模板 学生静态网页作业成品 dreamweaver电影HTML网站制作
    10道不得不会的 JavaEE 面试题
    【教学类-13-02】20221115《数字色块图5*7*8横板》(中班主题《》)
    虚拟机Ubuntu操作系统常用终端命令(2)(详细解释+详细演示)
    码农的转型之路-这款轮子可以造吗?
    Bert不完全手册2. Bert不能做NLG?MASS/UNILM/BART
    pytorch-实现猴痘识别
    Android 边播放边缓存视频框架AndroidVideoCache简析
    浅析kubernetes中client-go Informer
  • 原文地址:https://blog.csdn.net/qq_52245648/article/details/127823240