Redis服务端(1.3.6版本)初始化——配置加载部分
Redis服务端代码主要放在redis.c文件中。主要负责服务端业务逻辑实现,由上到下分为头文件引用、宏定义、数据结构、函数声明、全局变量、业务实现,几部分组成。本文主要讨论Redis服务端的初始化过程。
Redis服务端的main函数如下:
int main(int argc, char **argv) {
time_t start;
/* 初始化服务器配置 */
initServerConfig();
/* 加载配置 */
if (argc == 2) {
/* 重置服务端持久化参数 */
resetServerSaveParams();
/* 加载配置文件配置*/
loadServerConfig(argv[1]);
} else if (argc > 2) {
fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
exit(1);
} else {
redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");
}
/*设置守护进程方式运行 */
if (server.daemonize) daemonize();
/* 初始化服务器数据结构 */
initServer();
redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
/*
该文件指定了内核针对内存分配的策略,其值可以是0、1、2。
0,表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1,表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2,表示内核允许分配超过所有物理内存和交换空间总和的内存(参照overcommit_ratio)。
*/
linuxOvercommitMemoryWarning();
#endif
start = time(NULL);
/* 加载持久化数据 */
if (server.appendonly) {
if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from append only file: %ld seconds",time(NULL)-start);
} else {
if (rdbLoad(server.dbfilename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from disk: %ld seconds",time(NULL)-start);
}
redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
/* Redis启动部分 */
aeSetBeforeSleepProc(server.el,beforeSleep);
/* Redis主程序部分 */
aeMain(server.el);
/* Redis程序结束,清理数据 */
aeDeleteEventLoop(server.el);
return 0;
}
Redis的main函数逻辑主要是如下部分:
- 初始化Redis服务端基本配置。
- 从配置文件或者其他地方加载配置
- 按照配置,设置是否以守护方式进行
- 初始化服务器数据结构
- 判断Linux系统内存分配方式
- 从持久化文件中加载原有数据
- 使用多路复用API运行Redis主程序
Redis服务端配置数据结构
* Global server state structure */
struct redisServer {
/* 端口号 */
int port;
/* file description 文件描述符 多路复用API使用 */
int fd;
/* db数组,数据存储位置 */
redisDb *db;
dict *sharingpool; /* Poll used for object sharing */
unsigned int sharingpoolsize;
/* 距离上一次save,数据更改次数 */
long long dirty; /* changes to DB from the last save */
/* 连接的客户端数 */
list *clients;
/* 从节点和监听节点 */
list *slaves, *monitors;
char neterr[ANET_ERR_LEN];
/* 事件循环,多路复用API使用 */
aeEventLoop *el;
/* 定时任务循环 */
int cronloops; /* number of times the cron function run */
/* Redis的key-value内存释放不是立即释放,这样避免了频繁释放空间导致消耗资源过多 */
list *objfreelist; /* A list of freed objects to avoid malloc() */
/* 上一次save的时间 */
time_t lastsave; /* Unix time of last save succeeede */
/* Fields used only for stats */
/* 服务器启动时间 */
time_t stat_starttime; /* server start time */
/* 处理命令数 */
long long stat_numcommands; /* number of processed commands */
/* 连接数 */
long long stat_numconnections; /* number of connections received */
/* Configuration */
/* 配置部分 */
/* 日志级别 */
int verbosity;
/* Redis支持小数据打包成打数据进行打包发送,使用该配置开启 */
int glueoutputbuf;
/* 客户端最大空闲时间 */
int maxidletime;
/* 数据库个数 */
int dbnum;
/* 是否按照守护进程运行 */
int daemonize;
/* 是否每条修改都进行日志记录 */
int appendonly;
/* 更新日志策略
指定更新日志条件,共有 3 个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用 fsync() 将数据写到磁盘(慢,安全)
everysec:表示每秒同步一次(折中,默认值)
*/
int appendfsync;
/* 上一次更新时间 */
time_t lastfsync;
int appendfd;
int appendseldb;
/* pid文件位置 */
char *pidfile;
/* bgsave子进程pid */
pid_t bgsavechildpid;
/* bgwrite子进程pid */
pid_t bgrewritechildpid;
/* bgwrite buffer */
sds bgrewritebuf; /* buffer taken by parent during oppend only rewrite */
/* save参数 */
struct saveparam *saveparams;
int saveparamslen;
char *logfile;
char *bindaddr;
char *dbfilename;
/* aof文件名 */
char *appendfilename;
char *requirepass;
int shareobjects;
int rdbcompression;
/* Replication related */
int isslave;
char *masterauth;
char *masterhost;
int masterport;
redisClient *master; /* client that is master for this slave */
int replstate;
unsigned int maxclients;
unsigned long long maxmemory;
unsigned int blpop_blocked_clients;
unsigned int vm_blocked_clients;
/* Sort parameters - qsort_r() is only available under BSD so we
* have to take this state global, in order to pass it to sortCompare() */
int sort_desc;
int sort_alpha;
int sort_bypattern;
/* 以下部分先不考虑 */
/* Virtual memory configuration */
int vm_enabled;
char *vm_swap_file;
off_t vm_page_size;
off_t vm_pages;
unsigned long long vm_max_memory;
/* Hashes config */
size_t hash_max_zipmap_entries;
size_t hash_max_zipmap_value;
/* Virtual memory state */
FILE *vm_fp;
int vm_fd;
off_t vm_next_page; /* Next probably empty page */
off_t vm_near_pages; /* Number of pages allocated sequentially */
unsigned char *vm_bitmap; /* Bitmap of free/used pages */
time_t unixtime; /* Unix time sampled every second. */
/* Virtual memory I/O threads stuff */
/* An I/O thread process an element taken from the io_jobs queue and
* put the result of the operation in the io_done list. While the
* job is being processed, it's put on io_processing queue. */
list *io_newjobs; /* List of VM I/O jobs yet to be processed */
list *io_processing; /* List of VM I/O jobs being processed */
list *io_processed; /* List of VM I/O jobs already processed */
list *io_ready_clients; /* Clients ready to be unblocked. All keys loaded */
pthread_mutex_t io_mutex; /* lock to access io_jobs/io_done/io_thread_job */
pthread_mutex_t obj_freelist_mutex; /* safe redis objects creation/free */
pthread_mutex_t io_swapfile_mutex; /* So we can lseek + write */
pthread_attr_t io_threads_attr; /* attributes for threads creation */
int io_active_threads; /* Number of running I/O threads */
int vm_max_threads; /* Max number of I/O threads running at the same time */
/* Our main thread is blocked on the event loop, locking for sockets ready
* to be read or written, so when a threaded I/O operation is ready to be
* processed by the main thread, the I/O thread will use a unix pipe to
* awake the main thread. The followings are the two pipe FDs. */
int io_ready_pipe_read;
int io_ready_pipe_write;
/* Virtual memory stats */
unsigned long long vm_stats_used_pages;
unsigned lon