Redis服务器负责与多个客户端建立连接,处理客户端请求,保存各个数据库状态。通过使用由I/O多路复用技术实现的事件处理器,Redis服务器采用单线程单进程处理客户端命令请求。Redis通过redisServer结构来记录服务端的各种状态。
命令请求执行过程
1、客户端发送命令请求,客户端将命令请求转换成协议格式。
2、服务端读取命令请求,将命令请求缓存在客户端输入缓冲区中,对输入缓冲区中的命令进行分析把参数和参数个数分别保存到客户端状态的argv属性和argc属性中,然后调用命令执行器执行指定的命令。
3、命令执行器根据argv[0]参数在命令表中查找参数所指定的命令,并将找到的命令保存到客户端状态cmd属性中
4、执行预备操作:
5、调用命令的实现函数,redisCommand中有个proc属性,它是一个函数指针,通过调用它来执行最终的命令实现。
6、执行后续工作:
如果服务器开启了慢日志查询日志功能,那么慢查询日志模块会检查是否需要为刚刚执行完的命令请求添加一条新的慢查询日志; 根据执行命令所耗费的时长,更新被执行命令的redisCommand结构的milliseconds属性,并它的calls计数器值增1; 如果服务器开启了AOF持久化功能,那么AOF持久化模块会将刚刚执行的命令请求写入到AOF缓冲区里面; 如果有其它从服务器正在复制当前服务器,那么服务器将刚刚执行的命令传播给所有从服务器; 7、将命令回复发送给客户端,命令实现函数会将命令回复保存到客户端的输出缓冲区里面,并为客户端的套接字关联命令回复处理器,当客户端套接字变为可写状态时,服务器将会执行命令回复处理器,将保存在客户端缓冲区中的命令回复发送个客户端。当命令发送完毕后,回复处理器清空客户端输出缓冲区。(有个疑问,假设客户端没有尝试读取命令回复,那么服务端该如何处理该客户端的输出缓冲区?是在处理下个命令时被冲掉么?)。
serverCron函数
服务器每个100毫秒执行一次serverCron函数,通常情况下,该函数时redis服务器唯一的定时任务,它的主要职责如下:
1、更新服务器时间缓存,redisServer中有unixtime和mstime两个属性,由于100毫秒才调用一次,所以这两个属性保存的时间都是不精确的,如果要获取精确的系统时间,必须执行系统调用获取。
2、更新LRU时钟,redisServer中的lruclock属性保存了服务器的LRU时钟,它也是个服务器时间缓存,当服务器要计算一个数据库键的空转时间时,通过服务器的lruclock减redisObject的lru属性获取。serverCron默认以每10秒一次的频率更新lruclock,所以它也是一个估算值。
3、更新服务器每秒执行命令数。
4、更新服务器内存峰值,记录在redisServer的stat_peak_memory属性中。
5、处理SIGTERM信号,在启动服务器时,Redis会为服务器进程的SIGTERM信号关联处理器sigtermHandler函数,这个信号处理器负责在服务器接到SIGTERM信号时,打开服务器状态的shutdown_asap标识,每次serverCron函数运行时,程序会对服务器状态的shutdown_asap属性进行检查,并根据属性的值决定是否关闭服务器,在关闭服务器前服务器会进行RDB持久化操作,这就是服务器拦截SIGTERM信号的原因。
6、管理客户端资源,serverCron函数每次执行都会调用clientsCron函数, clientsCron函数会对一定数量的客户端进行一下两个检查:
8、执行被延迟的BGREWRITEAOF,服务器在执行BGSAVE期间,如果客户端向服务器请求了BGREWRITEAOF命令,那么服务器会将BGREWRITEAOF命令延迟到BGSAVE执行完毕之后执行,通过redisServer中的aof_rewrite_scheduled记录(值为1表示有BGREWRITEAOF命令被延迟了)。
9、检查持久化操作的运行状态,服务器通过redisServer的rdb_child_pid和aof_child_pid属性记录执行BGSAVE和BGREWRITEAOF命令的子进程id,这个两个属性也可用于检查当前是否有BGSAVE或BGREWRITEAOF命令正在执行,每次serverCron执行时都会检查者两个属性的值,只要有一个值不为-1,程序就会执行一次wait3函数,检查子进程是否有信号发来服务器:
如果有信号达到,表示新的RDB文件已经生成或者AOF文件重写已经完毕,服务器需要进行相应的后续操作,比如用新的RDB文件替换现有的RDB文件,或者用重写后的AOF文件替换现有的AOF文件 如果没有信号到达,那么表示持久化操作未完成,不做动作。 如果rdb_child_pid和aof_child