serverCron函数
更新服务器内存锋值记录
服务器状态中的stat_peak_memory属性记录了服务器的内存锋值大小:
struct redisServer {
// ...
// 已使用内存锋值
size_t stat_peak_memory;
// ..
};
每次serverCron函数执行时,程序都会查看服务器当前使用的内存数量,并与stat_peak_memory保存的数值进行比较,如果当前使用的内存数量比stat_peak_memory属性记录的值要大,那么程序就将当前使用的内存
数量记录到stat_peak_memory属性里面。INFO memory命令的used_memory_peak和used_memory_peak_human两个域分别以两种格式记录了服务器的内存锋值
used_memory:714200
used_memory_human:697.46K
used_memory_rss:677272
used_memory_peak:715040
used_memory_peak_human:698.28K
used_memory_lua:36864
mem_fragmentation_ratio:0.95
mem_allocator:jemalloc-3.6.0
处理SIGTERM信号。
在启动服务器时,Redis会为服务器进程的SIGTERM信号关联处理器sigtermHandler函数,这个信号处理器负责在服务器
接到SIGTERM信号时,打开服务器状态的shutdown_asap标识:
// SIGTERM信号的处理器
static void sigtermHandler(int sig) {
// 打印日志
redisLogFromHandler(REDIS_WARNING, "Received SIGTERM, scheduling shutdown ...");
// 打开关闭标识
server.shutdown_assap = 1;
}
每次serverCron函数运行时,程序都会对服务器状态的shutdown_asap属性进行检查,并根据属性的值决定是否关闭服务器:
struct redisServer {
// ...
// 关闭服务器的标识
// 1 关闭服务器
// 0 不做动作
int shutdown_asap;
// ...
}
服务器在关闭自身之前会进行RDB持久化操作,这也是服务器拦截SIGTERM信号的原因,如果服务器一接到SIGTERM信号就立即关闭,那么它就没办法执行持久化操作了
管理客户端资源。
serverCron函数每次执行都会调用clientsCron函数,clientsCron函数会对一定数量的客户端进行以下两个检查:
- 1.如果客户端与服务器之间的连接已经超时(很长一段时间里客户端和服务器都没有互动),那么程序释放这个客户端资源
- 2.如果客户端在上一次执行命令请求之后,输入缓冲区的大小超过了一定的长度,那么程序会释放客户端当前的输入缓冲区,并重新创建一个默认大小的输入缓冲区,从而防止客户端的输入缓冲区耗费了过多的内存
管理数据库资源。
serverCron函数每次执行都会调用databasesCron函数,这个函数会对服务器中的一部分数据库进行检查,删除其中的过期键,并在有需要时,对字典进行收缩操作
执行被延迟的BGREWRITEAOF
在服务器执行BGSAVE命令的期间,如果客户端向服务器发来BGREWRITEAOF命令,那么服务器会将BGREWRITEAOF命令的
执行时间延迟到BGSAVE命令执行完毕之后。服务器的aof_rewrite_scheduled标识记录了服务器是否延迟了BGREWRITEAOF
命令:
struct redisServer {
// ...
// 如果值为1,那么表示有 BGREWRITEAOF命令被延迟了
int aof_rewrite_scheduled;
// ...
}
每次serverCron函数执行时,函数都会检查BGSAVE命令或者BGREWRITEAOF命令是否正在执行,如果这两个命令都没在
执行,并且aof_rewrite_scheduled属性的值为1,那么服务器就会执行之前被推延的BGREWRITEAOF命令。
检查持久化操作的运行状态。
服务器状态使用rdb_child_pid属性和aof_child_pio属性记录执行BGSAVE命令和BGREWIRTEAOF命令的子进程的ID,这两个
属性也可以用于检查BGSAVE命令或者BGREWRITEAOF命令是否正在执行:
struct redisServer {
// ...
// 记录执行BGSAVE命令的子进程的ID
// 如果服务器没有在执行BGSAVE
// 那么这个属性的值为-1
pid_t rdb_child_pid // PID of RDB saving child
// 记录执行BGREWRITEAOF命令的子进程的ID:
// 如果服务器没有在执行BGREWRITEAOF
// 那么这个属性的值为-1
pid_t aof_child_pid // PID if rewriting process
}
每次serverCron函数执行时,程序都会检查rdb_child_pid和aof_child_pid两个属性的值,只要其中一个属性的值不为-1,程序
就会执行一次wait3函数,检查子进程是否有信号发来服务器进程:
- 1.如果有信号到达,那么表示新的RDB文件已经生成完毕(对于BGSAVE命令来说),或者AOF文件已经重写完毕(对于BGREWRITEAOF命令来说),服务器需要进行相应命令的后续操作,比如用新的RDB文件替换现有的RDB文件,或者用重写后的AOF文件替换现有的AOF文件
- 2.如果信号没有到达,那么表示持久化操作未完成,程序不做动作
另一方面,如果rdb_child_pid和aof_child_pid两个属性的值都为-1,那么表示服务器没有在进行持久化操作,在这种情况下,程序
执行三个检查: - 1.查看是否有BGREWRITEAOF被延迟了,如果有的话,那么开始一次新的BGREWRITEAOF操作
- 2.检查服务器的自动保存条件是否已经被满足,如果条件满足,并且服务器没有在执行其他持久化操作,那么服务器开始一次新的BGSAVE
操作(因为条件1可能会引发一次BGREWRITEAOF,所以在这个检查中,程序会再次确认服务器是否已经在执行持久化操作了) - 3.检查服务器设置的AOF重写条件是否满足,如果条件满足,并且服务器没有在执行其他持久化操作,那么服务器将开始一次新的
BGREWIRTEAOF操作(因为条件1和2都可能会引起新的持久化操作,所在这个检查中,我们要再次确认服务器是否已经在执行持久化操作)
整个检查过程如图所示。
将AOF缓冲区中的内容写入AOF文件。
如果服务器开启了AOF持久化功能,并且AOF缓冲区里面还有待写入的数据,那么serverCron函数会调用相应的程序,将AOF缓冲区中的内容
写入到AOF文件里面
增加cronloops计数器的值。
服务器状态的cronloops属性记录了serverCron函数执行的次数
struct redisServer {
// ...
// serverCron函数的运行次数计数器
// serverCron函数每执行一次,这个属性的值就增1
int cronloops;
// ...
}
cronloops属性目前在服务器中的唯一作用,就是在复制模块中实现"每执行serverCron函数N次就执行一次指定代码"的功能方法如以下伪代码所示
if cronloops % N == 0:
# 执行指定代码...