? ? ? ? 每個系統都會有類似一個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文件也存在我們意想不到的的設計。
- 前言
- (一)--Redis結構解析
- (二)--結構體分析(1)
- (三)---dict哈希結構
- (四)-- sds字符串
- (五)--- sparkline微線圖
- (六)--- ziplist壓縮列表
- (七)--- zipmap壓縮圖
- (八)--- t_hash哈希轉換
- (九)--- t_list,t_string的分析
- (十)--- testhelp.h小型測試框架和redis-check-aof.c日志檢測
- (十一)--- memtest內存檢測
- (十二)--- redis-check-dump本地數據庫檢測
- (十三)--- redis-benchmark性能測試
- (十四)--- rdb.c本地數據庫操作
- (十五)--- aof-append only file解析
- (十六)--- config配置文件
- (十七)--- multi事務操作
- (十八)--- db.c內存數據庫操作
- (十九)--- replication主從數據復制的實現
- (二十)--- ae事件驅動
- (二十一)--- anet網絡通信的封裝
- (二十二)--- networking網絡協議傳輸
- (二十三)--- CRC循環冗余算法和RAND隨機數算法
- (二十四)--- tool工具類(2)
- (二十五)--- zmalloc內存分配實現
- (二十六)--- slowLog和hyperloglog
- (二十七)--- rio系統I/O的封裝
- (二十八)--- object創建和釋放redisObject對象
- (二十九)--- bio后臺I/O服務的實現
- (三十)--- pubsub發布訂閱模式
- (三十一)--- latency延遲分析處理
- (三十二)--- redis-cli.c客戶端命令行接口的實現(1)
- (三十三)--- redis-cli.c客戶端命令行接口的實現(2)
- (三十四)--- redis.h服務端的實現分析(1)
- (三十五)--- redis.c服務端的實現分析(2)
- (三十六)--- Redis中的11大優秀設計