服务器
- 命令请求过程
- 具体实现过程
- serverCron函数
- 初始化服务器
命令请求过程
以SET命令为例
客户端向服务器发送:SET key value;——> 服务端接收并处理请求,并产生OK命令;
——> 服务端向客户端发送命令回复OK;——> 客户端接收并打印
具体实现过程
- 客户端发送请求命令
- 服务器执行命令
- 服务端将命令回复发送给客户端
serverCron函数
serverCron函数默认每隔100ms执行一次
- 更新服务器时间缓存
struct redisServer{
……
time_t unixtime; //保存秒级精度的系统当前UNIX时间戳
long long mstime; //保存毫秒级精度的系统当前UNIX时间戳
}
因为每100ms更新一次,所以时间精度并不高,只能用于打印日志、更新服务器的LRU时钟、决定是否持久化任务、计算服务器上线时间等对时间精度要求不高的功能上。而例如设置过期时间、添加慢查询等需要高精度时间的功能来说,服务器会再次执行系统调用获取当前时间。
- 更新LRU时钟
struct redisServer{
……
//保存服务器的LRU时钟,每10更新一次
unsigned lruclock:22;
}
//每隔对象都有一个lru属性,保存对象最后一次被命令访问的时间
typedef struct redisObject{
……
unsigned lru:22;
}
计算数据库键的空转时间: lruclock - lru
- 更新服务器每秒执行命令次数
struct redisServer{
...
//上次进行抽样的时间
long long ops_sec_last_sample_time;
//上次抽样时服务器已经执行命令的数量
long long ops_sec_last_sample_ops;
//数组中每个样都记录了一次抽样结果。数组是大小默认为16的环形数组
long long ops_sec_samples[REDIS_OPS_SEC_SAMPLES];
//ops_sec_samples数组的索引,每次抽样时将自增1,等于16时重置为0.
int ops_sec_index;
}
在serverCron函数中,trackOperationsPerSecond函数会每100ms就执行一次。
trackOperationsPerSecond根据当前服务器时间与上次抽样时间,以及服务器当前已执行命令数量与上次抽样时服务器已经执行命令的数量,计算出两次调用trackOperationsPerSecond之间服务器平均每一毫秒执行了多少个命令。这个结果乘以1000,就得到了服务器在一秒钟内处理的命令请求估计值,然后这个估计值就会放入数组ops_sec_samples中。
最终的instantaneous_ops_per_sec取得就是这个数组的平均值。即最近一秒钟服务器处理的命令请求数量。
- 更新服务器内存峰值记录
struct redisServer{
...
//已使用的内存峰值
size_t stat_peak_memory;
}
每次serverCron函数执行时,都会查看当前服务器的内存情况,并与stat_peak_memory进行比较,如果大于stat_peak_memory,则更新stat_peak_memory。
- 处理SIGTERM信号
struct redisServer{
...
//关闭服务器标识,0表示不作为,1表示关闭
int shutdown_asap
}
static void sigtermHandler(int sig){
//打印日志
redisLogFromHandler(REDIS_WARNING,"Received SIGTERM,scheduling shutdown ... ")
//打开关闭标识
server.shutdown_asap = 1;
}
服务器进程的SIGTERM信号关联处理器sigtermHandler函数,在服务器接收到SIGTERM信号时,打印相关日志并将shutdown_asap 标志设置为1。每次serverCron执行时会依据shutdown_asap决定是否关闭服务器。
- 管理客户端资源
serverCron函数每次执行都会调用clientsCron函数,这个函数会做两个检查:
如果客户端与服务器之间的连接已经超时(很长一段时间内没有互动),则释放这个客户端;
如果客户端上一次执行命令请求后,输入缓冲区大小超过了一定长度,那么程序就会释放客户端当前的输入缓冲区,并重新创建一个默认大小的输入缓冲区,从而防止客户端的输入缓冲区耗费过多内存。 - 管理数据库资源
serverCron函数每次执行会调用databasesCron函数,这个函数会对服务器中一部分数据库进行检查,删除过期键,并在需要时对字典进行收缩操作。 - 执行被延迟的BGREWRITEAOF
如果服务器在执行BGSAVE操作时,客户端向服务器发来BGREWRITRAOF命令,这个命令就会被延迟到BGSAVE命令执行完毕之后。
struct redisServer{
...
//如果为1,表示有BGREWRITRAOF命令被延迟了
int aof_rewrite_scheduled;
}
serverCron每次执行时,都会检查BGSAVE或BGREWRITEAOF命令是否正在执行,如果都没有执行,且aof_rewrite_scheduled值为1,就会执行之前被延迟的BGREWRITEAOF命令。
- 检查持久化运行状态
struct redisServer{
...
//记录执行BGSAVE命令的子进程ID,如果没有正在执行的BGSAVE,值为-1
pid_t rbd_child_pid;
//记录执行BGREWRITEAOF的子进程ID,如果没有正在执行的BGREWRITEAOF,值为-1
pid_t aof_child_pid;
}
serverCron每次执行时都会检查rbd_child_pid和aof_child_pid的属性值。
- 将AOF缓冲区中的内容写入AOF文件
如果服务器开启了AOF持久化功能,并且AOF缓冲区有待写入数据,则serverCron执行时会将AOF缓冲区的内容写入AOF文件里面。 - 关闭异步客户端
服务器会关闭那些输出缓冲区大小超过限制的客户端。 - 增加cronloops计数器的值
struct redisServer{
...
//记录了serverCron执行次数
int cronloops;
}
初始化服务器
- 初始化服务器状态结构
初始化server变了的工作有initServerConfig函数完成,其主要工作内容如下:
·设置服务器运行ID
·设置服务器默认运行频率
·设置服务器的默认配置文件路径
·设置服务器的运行架构
·设置服务器的默认端口号
·设置服务器的默认RDB持久化条件和AOF持久化的条件
·初始化服务器的LRU时钟
·创建命令表 - 载入配置选项
用户可以通过给定配置参数或者指定配置文件来修改服务器的默认配置。服务器在使用initServerConfig函数初始化完server变量之后,就开始载入用户给定的配置参数和配置文件,根据用户设定的配置,对server变量相关属性的值进行修改 - 初始化服务器数据结构
服务器调用initServer函数,为以下数据结构分配内存,在需要时可设置或关联初始化值。
· server.clients链表
· server.db数组
· server.pubsub_channels字典 && server.pubsub_patterns链表
· server.lua
· server.slowlog属性
除了初始化数据结构之外,还进行了一下操作:
· 为服务器设置进程信号处理器
· 创建共享对象
· 打开服务器监听端口,并为监听套接字关联连接应答事件处理器,等待服务器正式运行时接受客户端的连接。
· 为serverCron函数创建时间事件,等待服务器正式运行时执行serverCron函数
· 如果AOF持久化功能已打开,那么打开现有的AOF文件,如果AOF文件不存在,那么创建并打开一个新的AOF文件,为AOF写入做好准备
· 初始化服务器I/O模块。 - 还原数据库状态
如果服务器启用了AOF持久化功能,就使用AOF文件来还原数据,如果没有,使用RDB文件还原数据。 - 执行时间循环