Redis 源码分析之serverCron(定时循环)解析

Redis的serverCron大约是每1ms执行一次,并非严格1ms,一般来说,会有一定程度的延迟,

serverCron函数的三个参数,在函数内部都没有被使用,会有警告出来,所以使用REDIS_NOTUSED去除,不使用,为什么还传递这三个参数呢?莫非是为了格式的一致性么?

1.serverCron 设置 setitimer,超时的时候,进程会收到一个SIGALRM信号。redis 根据这个信号,打印 logStackTrace 信息,排查超时的原因。在生产环境,是不会做这种事情的。

2.redis更新全局事件变量,在要求不是很高的时候,通过读取该时间变量,默认为当前事件,可以节省时间,加快速度。

3.更新Lru时间,getLRUClock()函数如下
return (mstime()/REDIS_LRU_CLOCK_RESOLUTION) & REDIS_LRU_CLOCK_MAX;

翻译出来就是(mstime() / 1000) & ( (1 << 24 )- 1),这个时间,大概是380天的样子,lruclock每380天一个轮回。

4.如果之前收到了 SIGTERM 信号,并不会立即做什么事情,只是将server.shutdown_asap 置位,这里判断shutdown_asap , 调用prepareForShutdown ,关闭服务器,退出执行。但是如果没有退出成功,就不退出了,打印Log,然后移除标志位。

5.每50s,打印一次全部数据库的键值对,过期键的数量。

6.如果不是处于哨兵模式,每50s,记录一次客户端连接的数量,以及redis从库的数量。处于哨兵模式的时候,服务器所做的事情,就是监控其他服务器是否正常,它的客户端,就是几个固定的服务器 ,并没有打印客户端连接的必要。

7.clientsCron(),检查所有的客户端连接,是否有可以释放的,每次至少检查50个客户端,如果符合以下条件,就断开客户端连接。
redis越来越复杂了。

if (server.maxidletime &&
// 不检查作为从服务器的客户端
!(c->flags & REDIS_SLAVE) &&
// 不检查作为主服务器的客户端
!(c->flags & REDIS_MASTER) &&
// 不检查被阻塞的客户端
!(c->flags & REDIS_BLOCKED) &&
// 不检查订阅了频道的客户端
dictSize(c->pubsub_channels) == 0 &&
// 不检查订阅了模式的客户端,因为可能是监控器???
listLength(c->pubsub_patterns) == 0 &&
// 客户端最后一次与服务器通讯的时间已经超过了 maxidletime 时间
(now - c->lastinteraction > server.maxidletime))

同时检查这些客户端是不是可以缩小查询缓冲,节省内存。条件是 1.  查询缓冲区的大小大于 BIG_ARG 以及 querybuf_peak ,2.  客户端不活跃,并且缓冲区大于 1k的时候,减小缓冲区。怎么判断是否活跃呢,超过2s内没有任何交互,就判断为不活跃。

8. databasesCron(),定时脚本中,需要对db进行的操作,包括清除过期键以及判断是否需要rehash

9.检查aof和rdb是否进行的时候,使用了wait3(&statloc,WNOHANG,NULL))

WNOHANG:return immediately if no child has exited。通过非阻塞的方式查询子进程是否完成,而是通过每次cron查询的方式,大概是要做的事情太多了吧。rdb或者aof完成之后,执行backgroundSaveDoneHandler(exitcode,bysignal) 或者是 backgroundRewriteDoneHandler(exitcode,bysignal)

10.如果server.aof_flush_postponed_start置位,也即是每秒刷新aof文件,则刷新aof文件。
replicationCron,每10s执行一次, 重连接主服务器、向主服务器发送 ACK 、判断数据发送失败情况、断开本服务器超时的从服务器,等等

11.关闭需要异步关闭释放的客户端。

12.每10s,重新连接一次主服务器,发送ACK,断开超时的从服务器等。

13.如果服务器运行在集群模式下,那么每秒钟执行一次集群操作。

14.如果处于哨兵模式下, 每秒中执行一次sentinelTimer()

Leave a comment

Your email address will not be published.

*