Redis源码解析(十六)--- config配置文件

每个系统都会有类似一个config配置文件,config文件里的内容想想都知道,一定就是那么一些固定的一行行的属性代码了,今天在看redis代码中的config属性,那拉下来的一笔,的确多,目测在50至100个属性左右。如果就此将config每个属性代表什么意思不是我的风格,也一定是很乏味的,所以我的特点就是在代码中去理解程序员在写这类代码时的思路,和茫茫代码中的亮点。我们知道,redis运行的环境包括很多种的,windows,Linux,mac
os等等,不同的操作系统,当然有些属性就不能支持了,所以在redis中的config.h头文件中跟据计算机所属于的操作系统,做了很多的预处理,比如说,在Linux等系统上,是可以支持修改进程名称的:

/* Check if we can use setproctitle().
 * BSD systems have support for it, we provide an implementation for
 * Linux and osx. */
/* 检查是否能调用setproctitle(),这个方法是Linux上修改进程名称的方法 */
/* 这里通过判断是否是BSD系统,BSD是Berkeley Systems Distrobution的缩写,是一种UNIX版本 */
#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)
#define USE_SETPROCTITLE
#endif

#if (defined __linux || defined __APPLE__)
#define USE_SETPROCTITLE
#define INIT_SETPROCTITLE_REPLACEMENT
void spt_init(int argc, char *argv[]);
void setproctitle(const char *fmt, ...);
#endif

当然这只是我提到的一点,也就是说,redis在异构系统的处理上,考虑的还是非常周全的。我们主要看看config主要的操作文件,先看看里面的一些API:

 /* Config file API */
int yesnotoi(char *s) /* 判断字符是否为yes */
void appendServerSaveParams(time_t seconds, int changes) /* 追加server save参数 */
void resetServerSaveParams(void) /* 重置server的save参数,即释放server的serverParams */
void loadServerConfigFromString(char *config) /* 从字符串中加载server属性配置 */
void loadServerConfig(char *filename, char *options) /* 从文件中加载server配置 */
void configSetCommand(redisClient *c) /* 根据redisClient中的参数设置server的配置 */
#define config_get_string_field(_name,_var) /* 宏定义了获取字符串值域的方法 */
#define config_get_bool_field(_name,_var) /* 宏定义了获取布尔值域的方法,值在这里值为yes或no */
#define config_get_numerical_field(_name,_var) /* 宏定义了获取数字类型值域的方法 */
void configGetCommand(redisClient *c) /* 获取配置信息命令,以Replay给客户端的方式 */
void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) /* 添加配置字符串行 */
void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) /* 添加字典line-option */
void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, char *option) /* rewriteConfigState重写option选项 */
struct rewriteConfigState *rewriteConfigReadOldFile(char *path) /* 读取老配置文件信息,文件如果不可读或不存在,返回NULL */
void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force) /* 是否覆盖configline */
int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) /* 格式化byte大小的显示,避免long long 类型超长的显示 */
void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) /* 往config中写入某类型配置,后面的几个方法类似 */
void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) /* 同上 */
void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) /* 同上 */
void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) /* 同上 */
void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) /* 同上 */
void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, ...) /* 同上 */
void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) /* 同上 */
void rewriteConfigSaveOption(struct rewriteConfigState *state) /* 同上 */
void rewriteConfigDirOption(struct rewriteConfigState *state) /* 同上 */
void rewriteConfigSlaveofOption(struct rewriteConfigState *state) /* 同上 */
void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) /* 同上 */
void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) /* 同上 */
void rewriteConfigBindOption(struct rewriteConfigState *state) /* 同上 */
sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) /* confifstate中获取配置信息字符串 */
void rewriteConfigReleaseState(struct rewriteConfigState *state) /* configstate释放空间 */
void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) /* 置空state 的line配置 */
int rewriteConfigOverwriteFile(char *configfile, sds content) /* 字符串属性写入覆盖源文件 */
int rewriteConfig(char *path) /* 将当前的属性读入到文件中,步骤:(1).将当前server属性读入configstate(2).configstate属性变为字符串(3).将字符串写入文件 */
void configCommand(redisClient *c) /* 客户端config命令调用方法 */

上面的API的数量确实有点恐怖,我大体总结一下,在config.c中的主要操作:

1.从config配置文件中读取配置到server属性中

2.将当前server的设置的属性写入配置中

上面的其他方法都是为上面的2个要求服务的。所以redis在这里设计一个叫rewriteConfigState的结构体角色,里面保存了属性配置的字符串数组,一个字符串数组代表一种属性设置。比如现在要把当前的配置读入配置文件中操作:

int rewriteConfig(char *path) /* 将当前的属性读入到文件中,步骤:(1).将当前server属性读入configstate(2).configstate属性变为字符串(3).将字符串写入文件 */

下面看看redis代码中的这个configstate结构体的构造:

/* The config rewrite state. */
struct rewriteConfigState {
	//这里存放着option-line的映射
    dict *option_to_line; /* Option -> list of config file lines map */
    dict *rewritten;      /* Dictionary of already processed options */
    //当前配置文件中的行数
    int numlines;         /* Number of lines in current config */
    //当前行字符串
    sds *lines;           /* Current lines as an array of sds strings */
    int has_tail;         /* True if we already added directives that were
                             not present in the original config file. */
};

其中的lines就是具体的属性,第一个opition-line的映射指的是哪行对应什么属性名称。在config操作文件中,还提到了一个“maxmemory”的概念,中文意思可以了解为“最大记忆‘:

/* We use the following dictionary type to store where a configuration
 * option is mentioned in the old configuration file, so it's
 * like "maxmemory" -> list of line numbers (first line is zero). */
/* 下面定义了几个字典类型用来保存老的配置文件中的一些信息,像历史记录类似,like "maxmemory" */

也就是在redis文件中,可以存在老的配置文件,旧的属性可以通过老文件读入再次写入新配置中,达到了记录历史配置记录的作用。老配置文件读出的属性同样是存在于中间的configstate结构体中:

/* Read the old file, split it into lines to populate a newly created
 * config rewrite state, and return it to the caller.
 *
 * If it is impossible to read the old file, NULL is returned.
 * If the old file does not exist at all, an empty state is returned. */
/* 读取老配置文件信息,文件如果不可读或不存在,返回NULL */
struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
    FILE *fp = fopen(path,"r");
    struct rewriteConfigState *state = zmalloc(sizeof(*state));
    char buf[REDIS_CONFIGLINE_MAX+1];
    int linenum = -1;

    if (fp == NULL && errno != ENOENT) return NULL;

    state->option_to_line = dictCreate(&optionToLineDictType,NULL);
    state->rewritten = dictCreate(&optionSetDictType,NULL);
    state->numlines = 0;
    state->lines = NULL;
    state->has_tail = 0;
    if (fp == NULL) return state;

    /* Read the old file line by line, populate the state. */
    while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) {
        int argc;
        sds *argv;
        sds line = sdstrim(sdsnew(buf),"\r\n\t ");

        linenum++; /* Zero based, so we init at -1 */

        /* Handle comments and empty lines. */
        //处理注释和空行
        if (line[0] == '#' || line[0] == '\0') {
            if (!state->has_tail && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))
                state->has_tail = 1;
            rewriteConfigAppendLine(state,line);
            continue;
        }

        /* Not a comment, split into arguments. */
        argv = sdssplitargs(line,&argc);
        if (argv == NULL) {
            /* Apparently the line is unparsable for some reason, for
             * instance it may have unbalanced quotes. Load it as a
             * comment. */
            sds aux = sdsnew("# ??? ");
            aux = sdscatsds(aux,line);
            sdsfree(line);
            //将老的配置属性读入configstate结构体
            rewriteConfigAppendLine(state,aux);
            continue;
        }

        sdstolower(argv[0]); /* We only want lowercase config directives. */

        /* Now we populate the state according to the content of this line.
         * Append the line and populate the option -> line numbers map. */
        rewriteConfigAppendLine(state,line);
        rewriteConfigAddLineNumberToOption(state,argv[0],linenum);

        sdsfreesplitres(argv,argc);
    }
    fclose(fp);

    //返回configstate,里面记录了一些老的配置文件中的配置行信息
    return state;
}

又一次用到了configstate的结构体。小小config文件也存在我们意想不到的的设计。

时间: 2024-08-07 06:05:56

Redis源码解析(十六)--- config配置文件的相关文章

redis源码解析之内存管理

zmalloc.h的内容如下: 1 void *zmalloc(size_t size); 2 void *zcalloc(size_t size); 3 void *zrealloc(void *ptr, size_t size); 4 void zfree(void *ptr); 5 char *zstrdup(const char *s); 6 size_t zmalloc_used_memory(void); 7 void zmalloc_enable_thread_safeness(v

redis源码解析之事件驱动

Redis 内部有个小型的事件驱动,它主要处理两项任务: 文件事件:使用I/O多路复用技术处理多个客户端请求,并返回执行结果. 时间事件:维护服务器的资源管理,状态检查. 主要的数据结构包括文件事件结构体,时间事件结构体,触发事件结构体,事件循环结构体 /* File event structure */ typedef struct aeFileEvent { int mask; /* one of AE_(READABLE|WRITABLE) */ aeFileProc *rfileProc

Redis源码解析——双向链表

相对于之前介绍的字典和SDS字符串库,Redis的双向链表库则是非常标准的.教科书般简单的库.但是作为Redis源码的一部分,我决定还是要讲一讲的.(转载请指明出于breaksoftware的csdn博客) 基本结构 首先我们看链表元素的结构.因为是双向链表,所以其基本元素应该有一个指向前一个节点的指针和一个指向后一个节点的指针,还有一个记录节点值的空间 typedef struct listNode { struct listNode *prev; struct listNode *next;

Redis源码解析之ziplist

Ziplist是用字符串来实现的双向链表,对于容量较小的键值对,为其创建一个结构复杂的哈希表太浪费内存,所以redis 创建了ziplist来存放这些键值对,这可以减少存放节点指针的空间,因此它被用来作为哈希表初始化时的底层实现.下图即ziplist 的内部结构. Zlbytes是整个ziplist 所占用的空间,必要时需要重新分配. Zltail便于快速的访问到表尾节点,不需要遍历整个ziplist. Zllen表示包含的节点数. Entries表示用户增加上去的节点. Zlend是一个255

redis源码解析之dict数据结构

dict 是redis中最重要的数据结构,存放结构体redisDb中. typedef struct dict { dictType *type; void *privdata; dictht ht[2]; int rehashidx; /* rehashing not in progress if rehashidx == -1 */ int iterators; /* number of iterators currently running */ } dict; 其中type是特定结构的处

Spring Boot 启动源码解析系列六:执行启动方法一

1234567891011121314151617181920212223242526272829303132333435363738394041424344 public ConfigurableApplicationContext (String... args) { StopWatch stopWatch = new StopWatch(); // 开始执行,记录开始时间 stopWatch.start(); ConfigurableApplicationContext context =

Redis源码解析(十五)--- aof-append only file解析

继续学习redis源码下的Data数据相关文件的代码分析,今天我看的是一个叫aof的文件,这个字母是append ONLY file的简称,意味只进行追加文件操作.这里的文件追加记录时为了记录数据操作的改变记录,用以异常情况的数据恢复的.类似于之前我说的redo,undo日志的作用.我们都知道,redis作为一个内存数据库,数据的每次操作改变是先放在内存中,等到内存数据满了,在刷新到磁盘文件中,达到持久化的目的.所以aof的操作模式,也是采用了这样的方式.这里引入了一个block块的概念,其实就

Redis源码解析:15Resis主从复制之从节点流程

Redis的主从复制功能,可以实现Redis实例的高可用,避免单个Redis 服务器的单点故障,并且可以实现负载均衡. 一:主从复制过程 Redis的复制功能分为同步(sync)和命令传播(commandpropagate)两个操作: 同步操作用于将从节点的数据库状态更新至主节点当前所处的数据库状态: 命令传播操作则用于在主节点的数据库状态被修改,导致主从节点的数据库状态不一致时,让主从节点的数据库重新回到一致状态: 1:同步 当客户端向从节点发送SLAYEOF命令,或者从节点的配置文件中配置了

Redis源码解析:13Redis中的事件驱动机制

Redis中,处理网络IO时,采用的是事件驱动机制.但它没有使用libevent或者libev这样的库,而是自己实现了一个非常简单明了的事件驱动库ae_event,主要代码仅仅400行左右. 没有选择libevent或libev的原因大概在于,这些库为了迎合通用性造成代码庞大,而且其中的很多功能,比如监控子进程,复杂的定时器等,这些都不是Redis所需要的. Redis中的事件驱动库只关注网络IO,以及定时器.该事件库处理下面两类事件: a:文件事件(file  event):用于处理Redis

Redis源码解析——字符串map

本文介绍的是Redis中Zipmap的原理和实现.(转载请指明出于breaksoftware的csdn博客) 基础结构 Zipmap是为了实现保存Pair(String,String)数据的结构,该结构包含一个头信息.一系列字符串对(之后把一个"字符串对"称为一个"元素"(ELE))和一个尾标记.用图形表示该结构就是: Redis源码中并没有使用结构体来表达该结构.因为这个结构在内存中是连续的,而除了HEAD和红色背景的尾标记END(恒定是0xFF)是固定的8位,其