redis自顶向下源码阅读(三)——配置初始化
initServerConfig()
1. 设置服务器运行 ID
getRandomHexChars()
用SHA1
加密算法对ID进行加密,确保每个服务器的ID都不一样。即如果ID不同,那么它肯定是另一台服务器,或者是同一台机器重启了。
void getRandomHexChars(char *p, unsigned int len) {
char *charset = "0123456789abcdef";
unsigned int j;
/* Global state. */
static int seed_initialized = 0;
static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */
static uint64_t counter = 0; /* The counter we hash with the seed. */
// 对 SHA1 seed 进行初始化, SHA1 是一种加密算法
if (!seed_initialized) {
/* Initialize a seed and use SHA1 in counter mode, where we hash
* the same seed with a progressive counter. For the goals of this
* function we just need non-colliding strings, there are no
* cryptographic security needs. */
FILE *fp = fopen("/dev/urandom","r");
if (fp && fread(seed,sizeof(seed),1,fp) == 1)
seed_initialized = 1;
if (fp) fclose(fp);
}
if (seed_initialized) {
while(len) {
unsigned char digest[20];
SHA1_CTX ctx;
unsigned int copylen = len > 20 ? 20 : len;
SHA1Init(&ctx);
SHA1Update(&ctx, seed, sizeof(seed));
SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter));
SHA1Final(digest, &ctx);
counter++;
memcpy(p,digest,copylen);
/* Convert to hex digits. */
for (j = 0; j < copylen; j++) p[j] = charset[p[j] & 0x0F];
len -= copylen;
p += copylen;
}
} else {
/* If we can't read from /dev/urandom, do some reasonable effort
* in order to create some entropy, since this function is used to
* generate run_id and cluster instance IDs */
char *x = p;
unsigned int l = len;
struct timeval tv;
pid_t pid = getpid();
/* Use time and PID to fill the initial array. */
gettimeofday(&tv,NULL);
if (l >= sizeof(tv.tv_usec)) {
memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec));
l -= sizeof(tv.tv_usec);
x += sizeof(tv.tv_usec);
}
if (l >= sizeof(tv.tv_sec)) {
memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec));
l -= sizeof(tv.tv_sec);
x += sizeof(tv.tv_sec);
}
if (l >= sizeof(pid)) {
memcpy(x,&pid,sizeof(pid));
l -= sizeof(pid);
x += sizeof(pid);
}
/* Finally xor it with rand() output, that was already seeded with
* time() at startup, and convert to hex digits. */
for (j = 0; j < len; j++) {
p[j] ^= rand();
p[j] = charset[p[j] & 0x0F];
}
}
}
2. 给 server 赋值
// 设置服务器的运行 ID
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
// 设置默认配置文件路径
server.configfile = NULL;
// 设置默认服务器频率
server.hz = REDIS_DEFAULT_HZ;
// 为运行 ID 加上结尾字符
server.runid[REDIS_RUN_ID_SIZE] = '\0';
// 机器的位数
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
// 服务器端口号
server.port = REDIS_SERVERPORT;
// TCP连接中已完成队列(完成三次握手之后)的长度
server.tcp_backlog = REDIS_TCP_BACKLOG;
/* 中间全是赋值,先略过,用到的时候再说
* 。。。
*/
server.loading_process_events_interval_bytes = (1024*1024*2);
server.lruclock = getLRUClock();
// 重置 server.saveparams, 清空, 归零
resetServerSaveParams();
// 重新配置 server.saveparams
appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */
appendServerSaveParams(300,100); /* save after 5 minutes and 100 changes */
appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes */
/* 给复制相关的参数赋值 */
server.masterauth = NULL;
server.masterhost = NULL;
server.masterport = 6379;
server.master = NULL;
server.cached_master = NULL;
server.repl_master_initial_offset = -1;
server.repl_state = REDIS_REPL_NONE;
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA;
server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY;
server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;
server.repl_diskless_sync = REDIS_DEFAULT_REPL_DISKLESS_SYNC;
server.repl_diskless_sync_delay = REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY;
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
server.master_repl_offset = 0;
/* Replication partial resync backlog */
// 初始化 PSYNC 命令所使用的 backlog
server.repl_backlog = NULL;
server.repl_backlog_size = REDIS_DEFAULT_REPL_BACKLOG_SIZE;
server.repl_backlog_histlen = 0;
server.repl_backlog_idx = 0;
server.repl_backlog_off = 0;
server.repl_backlog_time_limit = REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT;
server.repl_no_slaves_since = time(NULL);
/* Client output buffer limits */
// 设置客户端的输出缓冲区限制
for (j = 0; j < REDIS_CLIENT_TYPE_COUNT; j++)
server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];
/* Double constants initialization */
// 初始化浮点常量
R_Zero = 0.0;
R_PosInf = 1.0/R_Zero;
R_NegInf = -1.0/R_Zero;
R_Nan = R_Zero/R_Zero;
3. 初始化命令表
3.1. 创建命令表(空)
- 根据
commandTableDictType
给server
创建命令表,此时只是一个空表,
server.commands = dictCreate(&commandTableDictType,NULL);
server.orig_commands = dictCreate(&commandTableDictType,NULL);
-
commandTableDictType
指定了
commandTable
的DictType
,
/* Command table. sds string -> command struct pointer. */
dictType commandTableDictType = {
dictSdsCaseHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictSdsKeyCaseCompare, /* key compare */
dictSdsDestructor, /* key destructor */
NULL /* val destructor */
};
-
dictSdsCaseHash
把字符串(sds)转换成小写,再哈希(哈希不重要),所以命令是不区分大小写
-
dictSdsKeyCaseCompare
对比两个字符串是否相等,暂时不知道怎么在哪用。
-
dictSdsDestructor
3.2. 批量生成命令
-
把
redisCommandTable
中的各种命令初始化,并插入上面生成的表中sever.commands
void populateCommandTable(void) {
int j;
// 命令的数量
int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
for (j = 0; j < numcommands; j++) {
// 指定命令
struct redisCommand *c = redisCommandTable+j;
// 取出字符串 FLAG
char *f = c->sflags;
int retval1, retval2;
// 根据字符串 FLAG 生成实际 FLAG
while(*f != '\0') {
switch(*f) {
case 'w': c->flags |= REDIS_CMD_WRITE; break;
case 'r': c->flags |= REDIS_CMD_READONLY; break;
case 'm': c->flags |= REDIS_CMD_DENYOOM; break;
case 'a': c->flags |= REDIS_CMD_ADMIN; break;
case 'p': c->flags |= REDIS_CMD_PUBSUB; break;
case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
case 'R': c->flags |= REDIS_CMD_RANDOM; break;
case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;
case 'l': c->flags |= REDIS_CMD_LOADING; break;
case 't': c->flags |= REDIS_CMD_STALE; break;
case 'M': c->flags |= REDIS_CMD_SKIP_MONITOR; break;
case 'k': c->flags |= REDIS_CMD_ASKING; break;
case 'F': c->flags |= REDIS_CMD_FAST; break;
default: redisPanic("Unsupported command flag"); break;
}
f++;
}
// 将命令关联到命令表
retval1 = dictAdd(server.commands, sdsnew(c->name), c);
/* Populate an additional dictionary that will be unaffected
* by rename-command statements in redis.conf. */
retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
redisAssert(retval1 == DICT_OK && retval2 == DICT_OK);
}
}
-
redisCommandTable
其中每一条表示一个命令,命令中参数的意义见
redisCommand
。
struct redisCommand redisCommandTable[] = {
{"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
{"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},
// 以下省略
};
redisCommand
struct redisCommand {
// 命令名称,在server.commands命令表中,以命令名位key
char *name;
// 命令处理函数
redisCommandProc *proc;
// command的操作数,>0时表示确切的操作数,<0则表示至少有arity个操作数
int arity;
// 标记位的字符表示形式,主要用于命令表的初始化
char *sflags; /* Flags as string representation, one char per flag. */
// 属性标记位,bitwise,指定command的类型
int flags; /* The actual flags, obtained from the 'sflags' field. */
// 下面的4个属性都是用于从客户端的一个请求解析出key,比如mset k1, v1, k2, v2 ...
/* Use a function to determine keys arguments in a command line. */
redisGetKeysProc *getkeys_proc;
/* What keys should be loaded in background when calling this command? */
int firstkey; /* The first argument that's a key (0 = no keys) */
int lastkey; /* The last argument that's a key */
int keystep; /* The step between first and last key */
// 统计信息
long long microseconds, calls;
};
3.3. 把一些命令赋值给sever
server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi");
server.lpushCommand = lookupCommandByCString("lpush");
server.lpopCommand = lookupCommandByCString("lpop");
server.rpopCommand = lookupCommandByCString("rpop");
4. 其余的一些系统设置
/* Slow log */
server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
/* Latency monitor */
server.latency_monitor_threshold = REDIS_DEFAULT_LATENCY_MONITOR_THRESHOLD;
/* Debugging */
server.assert_failed = "<no assertion failed>";
server.assert_file = "<no file>";
server.assert_line = 0;
server.bug_report_start = 0;
server.watchdog_period = 0;
}