本文基于社区版Redis 4.0.8
 
 
 

1、 初始化参数配置

由函数initServerConfig()实现,具体操作就是给配置参数赋初始化值:
//设置时区
setlocale(LC_COLLATE,"");
//设置随机种子
char hashseed[16];
getRandomHexChars(hashseed,sizeof(hashseed));
dictSetHashFunctionSeed((uint8_t*)hashseed);
//初始化module
void initServerConfig(void) {
//serverCron函数执行频率,默认10ms
server.hz = CONFIG_DEFAULT_HZ;
//监听端口,默认6379
server.port = CONFIG_DEFAULT_SERVER_PORT;
server.tcp_backlog = CONFIG_DEFAULT_TCP_BACKLOG;
server.dbnum = CONFIG_DEFAULT_DBNUM;
......
//初始化命令表
server.commands = dictCreate(&commandTableDictType,NULL);
server.orig_commands = dictCreate(&commandTableDictType,NULL);
populateCommandTable();
server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi");
server.lpushCommand = lookupCommandByCString("lpush");
server.lpopCommand = lookupCommandByCString("lpop");
server.rpopCommand = lookupCommandByCString("rpop");
server.sremCommand = lookupCommandByCString("srem");
server.execCommand = lookupCommandByCString("exec");
server.expireCommand = lookupCommandByCString("expire");
server.pexpireCommand = lookupCommandByCString("pexpire");
......
}
 

2、 加载并解析配置文件

在这一阶段,会对命令行传入的参数进行解析,并且调用 loadServerConfig 函数,对命令行参数和配置文件中的参数进行合并处理,然后为 Redis 各功能模块的关键参数设置合适的取值,以便 server 能高效地运行。
//filename表示配置文件全路径名称;
//options表示命令行输入的配置参数,如port=4000
void loadServerConfig(char *filename, char *options) {
sds config = sdsempty();
char buf[CONFIG_MAX_LINE+1]; /* 加载配置文件到内存 */
if (filename) {
FILE *fp; if (filename[0] == '-' && filename[1] == '\0') {
fp = stdin;
} else {
if ((fp = fopen(filename,"r")) == NULL) {
serverLog(LL_WARNING,
"Fatal error, can't open config file '%s'", filename);
exit(1);
}
}
while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL)
config = sdscat(config,buf);
if (fp != stdin) fclose(fp);
}
/* Append the additional options */
if (options) {
config = sdscat(config,"\n");
config = sdscat(config,options);
}
//解析配置
loadServerConfigFromString(config);
sdsfree(config);
}

3、 初始化服务器内部变量

在完成对运行参数的解析和设置后,main 函数会调用 initServer 函数,对 server 运行时的各种资源进行初始化工作。包括了 server 资源管理所需的数据结构初始化、键值对数据库初始化、server 网络框架初始化等。
最后调用 loadDataFromDisk 函数,从磁盘上加载 AOF 或者是 RDB 文件,以便恢复之前的数据。
void initServer(void) {
/* 初始化需要的各种资源 */
server.clients = listCreate();//初始化客户端链表
server.pid = getpid();
server.current_client = NULL;
server.clients = listCreate();
server.clients_to_close = listCreate();
server.slaves = listCreate();
server.monitors = listCreate();
server.clients_pending_write = listCreate();
server.slaveseldb = -1; /* Force to emit the first SELECT command. */
server.unblocked_clients = listCreate();
server.ready_keys = listCreate();
server.clients_waiting_acks = listCreate();
server.get_ack_from_slaves = 0;
server.clients_paused = 0;
server.system_memory_size = zmalloc_get_memory_size(); createSharedObjects();
//调用aeCreateEventLoop函数创建aeEventLoop结构体,并赋值给server结构的el变量
//maxclients 变量的值大小,可以在 Redis 的配置文件 redis.conf 中进行定义,默认值是 1000
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
if (server.el == NULL) {
serverLog(LL_WARNING,
"Failed creating the event loop. Error message: '%s'",
strerror(errno));
exit(1);
}
...... /* 创建数据库结构*/
for (j = 0; j < server.dbnum; j++) {
server.db[j].dict = dictCreate(&dbDictType,NULL);
server.db[j].expires = dictCreate(&keyptrDictType,NULL);
server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);
server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
server.db[j].id = j;
server.db[j].avg_ttl = 0;
} ...... //创建事件循环框架
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);

//开始监听设置的网络端口
if (server.port != 0 &&
listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
exit(1);

//为server后台任务创建定时事件
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.");
exit(1);
}

//为每一个监听的IP设置连接事件的处理函数acceptTcpHandler
for (j = 0; j < server.ipfd_count; j++) {
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR)
{ … }
}
}

4、执行事件驱动框架

事件驱动框架是 Redis server 运行的核心。该框架一旦启动后,就会一直循环执行,每次循环会处理一批触发的网络读写事件。main 函数直接调用事件框架的主体函数 aeMain(在ae.c文件中)后,就进入事件处理循环了。当然,在进入事件驱动循环前,main 函数会分别调用 aeSetBeforeSleepProc 和 aeSetAfterSleepProc 两个函数,来设置每次进入事件循环前 server 需要执行的操作,以及每次事件循环结束后 server 需要执行的操作。下面代码显示了这部分的执行逻辑,你可以看下。
 
aeSetBeforeSleepProc(server.el,beforeSleep);
aeSetAfterSleepProc(server.el,afterSleep);
aeMain(server.el);
aeDeleteEventLoop(server.el);
 
 
 
 

【源码】Redis Server启动过程的更多相关文章

  1. 曹工说Redis源码(3)-- redis server 启动过程完整解析(中)

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  2. 曹工说Redis源码(5)-- redis server 启动过程解析,以及EventLoop每次处理事件前的前置工作解析(下)

    曹工说Redis源码(5)-- redis server 启动过程解析,eventLoop处理事件前的准备工作(下) 文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis ...

  3. 曹工说Redis源码(2)-- redis server 启动过程解析及简单c语言基础知识补充

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  4. workerman源码分析之启动过程

    PHP一直以来以草根示人,它简单,易学,被大量应用于web开发,非常可惜的是大部分开发都在简单的增删改查,或者加上pdo,redis等客户端甚至分布式,以及规避语言本身的缺陷.然而这实在太委屈PHP了 ...

  5. 跟着大彬读源码 - Redis 1 - 启动服务,程序都干了什么?

    一直很羡慕那些能读 Redis 源码的童鞋,也一直想自己解读一遍,但迫于 C 大魔王的压力,解读日期遥遥无期. 相信很多小伙伴应该也都对或曾对源码感兴趣,但一来觉得自己不会 C 语言,二来也不知从何入 ...

  6. Nimbus<三>Storm源码分析--Nimbus启动过程

    Nimbus server, 首先从启动命令开始, 同样是使用storm命令"storm nimbus”来启动看下源码, 此处和上面client不同, jvmtype="-serv ...

  7. 【Kafka源码】KafkaController启动过程

    [TOC] 之前聊过了很多Kafka启动过程中的一些加载内容,也知道了broker可以分为很多的partition,每个partition内部也可以分为leader和follower,主从之间有数据的 ...

  8. 【Kafka源码】Kafka启动过程

    一般来说,我们是通过命令来启动kafka,但是命令的本质还是调用代码中的main方法,所以,我们重点看下启动类Kafka.源码下下来之后,我们也可以通过直接运行Kafka.scala中的main方法( ...

  9. spark源码阅读--SparkContext启动过程

    ##SparkContext启动过程 基于spark 2.1.0  scala 2.11.8 spark源码的体系结构实在是很庞大,从使用spark-submit脚本提交任务,到向yarn申请容器,启 ...

随机推荐

  1. Docker&K8S学习笔记(一)—— Docker安装

    最近一年在工作上经常使用Docker与K8S,除了利用其打镜像,部署服务外,还基于Docker与K8S开发了一套CICD流水线平台,为了加深相关知识点的理解,所以从今天开始会定期更新学习笔记,本套学习 ...

  2. leetcode日记本

    写在前面: 2019.6开始经过一年的学习,我依然没有学会算法,依然停留在最基本的阶段,面对题目依然一头雾水 但是难不是放弃的理由,根据毛主席的论持久战原理,我决定一天看一点循序渐进,相信总有一天可以 ...

  3. [Kaiming]Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification

    目录 概 主要内容 PReLU Kaiming 初始化 Forward case Backward case He K, Zhang X, Ren S, et al. Delving Deep int ...

  4. 如何优雅地读写HttpServletRequest和HttpServletResponse的请求体

    最近很多交互要同原生的HttpServletRequest和HttpServletResponse打交道.从HttpServletRequest中读取body数据封装成某种数据结构:向HttpServ ...

  5. CS5211|CS5211参数|eDP转LVDS转换器芯片

    CS5211概述 CS5211是一个eDP到LVDS转换器,配置灵活,适用于低成本显示系统.CS5211与eDP 1.2兼容,支持1车道和2车道模式,每车道速度为1.62Gbps和2.7Gbps.CS ...

  6. 使用 JavaScript 中的变量、数据类型和运算符,计算出两个 number 类型的变量与一个 string 类型的变量的和,根据 string 类型处于运算符的不同位置得到不同的结果

    查看本章节 查看作业目录 需求说明: 使用 JavaScript 中的变量.数据类型和运算符,计算出两个 number 类型的变量与一个 string 类型的变量的和,根据 string 类型处于运算 ...

  7. YangTools从YANG生成Java类(Maven)

    1.说明 ODL提供了Yang Tools工具从YANG文件生成Java类, 本文介绍使用Maven插件的方式生成, 基于yang-maven-plugin这个插件. 2.创建Maven工程 Ecli ...

  8. eDiary电子日记本

    1.简介 eDiary是一款小巧的本地电子日记本, 也可以用来管理资料文档, 支持常用的文字编辑排版功能, 也支持插入图片功能. 支持多用户,可以设置登录用户名和密码, 每个用户可拥有多个日记文件, ...

  9. Linux配置yum源(离线和在线)

    配置yum源有2种方法,一种是离线yum源,另外一种是在线yum源. 一.离线yum源,基于安装光盘提供的安装仓库. 建立一个属于仓库文件夹 mkdir /media/zidong cd /media ...

  10. Flask + flask_sqlalchemy + jq 完成书籍展示、新增、删除功能

    后端代码 from flask import Flask, render_template, request, jsonify from flask_wtf.csrf import CSRFProte ...