• Redis源码分析(十)--- testhelp.h小型测试框架和redis-check-aof.c日志检测


                分析了一段时间的struct结构体的redis代码,越到最后越发现其实很多代码都是大同小异的。在struct包中还有1,2个文件还没分析,是关于set集合的一些东西,就放在下次分析好了,在选择下个分析的对象时,我考虑了一下,最后决定先把简单的test包下的东西看看一下,毕竟结构体这块有点复杂,所以这次分析个简单点的。test包下的文件并不多,代码量也很少,总共5个文件:

    1.memtest.c 内存检测
    2.redis_benchmark.c 用于redis性能测试的实现。
    3.redis_check_aof.c 用于更新日志检查的实现。
    4.redis_check_dump.c 用于本地数据库检查的实现。
    5.testhelp.c 一个C风格的小型测试框架。

    今天讨论3和5,先看看5,testhelp.c被解释为一个C风格的小型测试框架,看见这标题,一定想到这是一个很大的文件吧,都被称为测试框架了,其实不然,我贴出整个代码:

    /* 预处理 */
    #ifndef __TESTHELP_H
    #define __TESTHELP_H
    
    /* 开始时失败总数为0 */
    int __failed_tests = 0;
    /* 开始时测试总的例子数为0 */
    int __test_num = 0;
    
    /* 宏定义测试方法,输入参数,输入描述语,判断的式子作为参数 */
    /* 有完全体现了函数式编程的思想 */
    #define test_cond(descr,_c) do { 
        __test_num++; printf("%d - %s: ", __test_num, descr); 
        //判断式子在此处调用
        if(_c) printf("PASSED
    "); else {printf("FAILED
    "); __failed_tests++;} 
    } while(0);
    
    /* 测试报告在最结尾输出 */
    #define test_report() do { 
        printf("%d tests, %d passed, %d failed
    ", __test_num, 
                        __test_num-__failed_tests, __failed_tests); 
        if (__failed_tests) { 
            printf("=== WARNING === We have failed tests here...
    "); 
            exit(1); 
        } 
    } while(0);
    
    #endif
    

    调用形式也是极其简单:

     * test_cond("Check if 1 == 1", 1==1)
     * test_cond("Check if 5 > 10", 5 > 10)
     * test_report()

    其实就是宏定义了一个判别表达式真假的方法,又用到了函数式编程的思想,把_c整个表达式的值传入了另一个函数中,就这么简单。好,下一个文件redis_check_aof.c用于日志检测的,必然和文件的操作相关的,同样列出里面的API:

    /* 方法API */
    int consumeNewline(char *buf) /* 消除buf前面的换行符,即比较buf字符串中的前2个字符 */
    int readLong(FILE *fp, char prefix, long *target) /* 从文件中读取long类型值 */
    int readBytes(FILE *fp, char *target, long length) /* 从文件中读取字节 */
    int readString(FILE *fp, char** target) /* 文件中读取字符串 */
    int readArgc(FILE *fp, long *target) /* 文件中读取参数,首字符以“*”开头 */
    off_t process(FILE *fp) /* 返回fp文件的偏移量 */

    read的很多操作其实都是非常类似的,我就举出其中的一个read方法当做例子,就是纯粹文件的简单操作,熟悉C语言的同学一定很熟悉:

    /* 从文件中读取long类型值 */
    int readLong(FILE *fp, char prefix, long *target) {
        char buf[128], *eptr;
        //定位到文件的读取位置
        epos = ftello(fp);
        //将文件中的内容读取到buf中
        if (fgets(buf,sizeof(buf),fp) == NULL) {
        	//如果为空直接返回
            return 0;
        }
        
        //如果读取到的首字符不等于预期值,则提示报错
        if (buf[0] != prefix) {
            ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
            return 0;
        }
        
        //将字符串值转成long类型值
        *target = strtol(buf+1,&eptr,10);
        
        return consumeNewline(eptr);
    }
    

    里面有一个比较特别的方法,consumeNewline()消除换行符的方法:

    /* 消除buf前面的换行符,即比较buf字符串中的前2个字符 */
    int consumeNewline(char *buf) {
        if (strncmp(buf,"
    ",2) != 0) {
        	//如果不是等于"
    ",则提示出错
            ERROR("Expected \r\n, got: %02x%02x",buf[0],buf[1]);
            return 0;
        }
        return 1;
    }

    里面比较复杂的方法是off_t process(FILE *fp)获取文件偏移量的操作,这个方法是用来后面截断文件的操作,截断后面的空文件的部分:

    off_t pos = process(fp);
    
    //截断文件的操作,从问价头部到后面的偏移量,没有用的空间截去
                if (ftruncate(fileno(fp), pos) == -1) {
                    printf("Failed to truncate AOF
    ");
                    exit(1);
                } else {
                    printf("Successfully truncated AOF
    ");
                }

    以上就是我所分析的内容了,非常简单,比起struct比,简单太多了,可以细细体会其中方法涉及的技巧,比如我们会考虑日志文件分析时会想到consumeNewline()换行操作的设计吗
  • 相关阅读:
    gost源码分析心得
    go语言net编程,设置TCP连接发出使用源IP
    代理程序gost使用
    squid关闭缓存
    shell中的if比较
    10年以上年化20%以上收益率的基金经理
    股票信息查询
    02.win2003虚拟机安装和dos命令
    01.网络安全和虚拟机
    部署kali渗透环境
  • 原文地址:https://www.cnblogs.com/bianqi/p/12184269.html
Copyright © 2020-2023  润新知