Redis服务器负责与多个客户端创建网络通讯,处理客户端发送的命令请求,在数据库中保存客户端执行命令所产生的数据,并经过资源管理来维持服务器自身的运转。redis
命令请求过程(以set命令为例)数据库
一、客户端向服务器发送命令请求 SET KEY VALUE。数组
Redis服务器的命令请求来自于Redis客户端,当用户从客户端键入一个命令请求时,客户端会将这个命令命令请求请求转换成协议格式,而后经过链接到服务器的套接字,将协议格式的命令请求发送给服务器。缓存
二、服务器接收并处理客户端发送来的命令请求 SET KEY VALUE,在数据库中进行设置操做,并产生命令回复OK。服务器
读取套接字中的协议格式命令请求,并将其保存在客户端状态的输入缓冲区中。网络
对输入缓冲区中的命令请求进行解析,提取出命令请求中包含的命令参数,以及命令参数的格式,分别将参数和参数个数保存到客户端状态的argv和argc属性中数据结构
调用命令执行器,执行客户端指定的命令。架构
命令执行器:app
一、查找命令。二、执行预备操做。(检查命令、命令参数、客户端身份验证、检查服务器内存占用状况、事务、服务器状态、是否监听等)三、调用命令实现函数,产生命令回复函数,保存在客户端状态的输出缓存区四、执行后续工做:(慢查询检查记录日志;根据命令耗时更新redisCommand结构的milliseconds属性、calls计数器加1;若是开启了AOF,若是是写命令则写入到AOF缓冲区;若是有其余从服务器正在复制当前的服务器,那么服务器会将刚刚执行的命令传播给全部从服务器。)异步
三、服务器将命令回复OK发送为客户端。
当客户端套接字变为可写状态时,服务器会执行命令回复处理器,将保存在客户端缓冲区的命令回复发送给客户端。
四、客户端接收服务器返回的命令回复OK,并将这个回复打印给用户观看。客户端收到协议格式的命令回复后,将其转换为人类可读的格式,并打印。
serverCron函数
Redis服务器中的serverCron函数每100毫秒执行一次,这个函数负责管理服务器的资源,并保持服务器自身的良好运转。
一、更新服务器缓存时间,为减小系统调用获取当前时间的次数,服务器状态中的unixtime和mstime属性被用做当前时间的缓存:
struct redisServer{ //保存了秒级精度的当前unix时间戳 time_t unixtime; //保存了毫秒级精度的系统当前unix时间戳 long long mstime; //默认每10秒更新一次事件缓存,用于计算键的空转时间。 unsigned lruclock:22 }
服务器只会在打印日志、更新服务器LRU始终、决定是否执行持久化任务、计算服务器上线时间这类对事件精度不高的功能上用;对于为键设置过时事件、添加慢日志这种须要高精度事件的功能来讲,服务器仍是会再次执行系统调用,从而得到更准确的系统当前事件。
每一个Redis对象都有一个lru属性,这个lru属性保存了对象最后一次被命令访问的时间:
typedef struct redisObject{ unsigned lru:22' } robj;
serverCron函数中的trackOperationsPerSecond函数会以每100毫秒一次的频率执行,这个函数的功能以抽样计算的方式,估算并记录服务器在最近一秒钟处理的命令请求数量,这个值经过INFO status命令的instantaneous_ops_per_sec域查看。
二、更新服务器内存峰值记录:服务器状态中的stat_peak_memory属性记录服务器的内存峰值大小:
struct redisServer{ //已使用内存峰值 size_t stat_peak_memory; }
三、处理sigterm信号
static void sigtermHandler(int sig){ //打印日志 redisLogFromHandler(REDIS_WARNING,"received sigterm,scheduling shutdown..."); //打开关闭标识 server.shutdown_asap=1; }
struct redisServer{ //serverCron函数运行时,程序灰度服务器状态的shutdown_asap属性进行检查,并根据属性值决定是否关闭服务器。 //关闭服务器的标识 //值为1时,关闭服务器 //值为0时,不作动做 int shutdown_asap; }
四、管理客户端资源
serverCron函数每次执行都会调用clientCron函数,clientsCron函数会对必定数量的客户端进行检查:若是客户端与服务器之间的链接已经超时,那么程序释放这个客户端;若是客户端在上一次执行命令请求后,输入缓冲区的大小超过了必定的长度,那么程序会释放客户端当前的输入缓冲区,从新建立一个迷人大小的输入缓冲区,防止客户端的输入缓冲区耗费过多的资源。
五、管理数据库资源 :删除过时键,若有须要,对字典进行收缩操做。
六、执行被延迟的BGREWRITEAOF
若是BGSAVE执行期间,客户端发来BGREWRITEAOF命令,则须要延迟到BGSAVE命令执行完成后。
七、检查持久化操做运行状态
服务器状态使用rdb_child_pid\aof_child_pid属性记录执行BGSAVe命令和BGREWRITEAOF命令的子进程ID,用于检查命令是否正在执行
struct redisServer{ //记录执行BGSAVE命令的紫禁城,若是没有执行则为-1 pid_t rdb_child_pid; //记录执行BGREWRITEAOF命令的子进程ID,没执行则为-1 pid_t aof_child_pid; }
八、将AOF缓冲区中的内容写入AOF文件
九、关闭异步客户端(检查输出缓冲区大小)
十、增长cronloops计数器的值(没执行serverCron函数N次就执行一次指定的代码)
初始化服务器
一、初始化服务器状态结构,建立一个struct redisServer类型的实例变量server做为服务器的状态。
initServerConfig函数完成的主要工做:
设置服务器运行ID
设置服务器的默认运行频率
设置服务器的默认配置文件路径
设置服务器的运行架构
设置服务器的默认端口号
设置服务器默认RDB持久化条件和APF持久化条件
初始化服务器LRU时钟
建立命令表
二、载入配置项修改默认的配置。
三、初始化服务器数据结构
在执行initServerConfig函数初始化server状态时,程序只建立命令表一个数据结构,服务器在次数初始化其余数据结构:
server.clients链表,记录全部与服务器相链接的客户端状态
erver.db数组,包含服务器的全部数据库。
保存频道订阅信息的server.pubsub_channels字典以及保存模式订阅信息的server_publsub_patterns链表。
执行Lua脚本的Lua环境 server.lua;
用于保存慢查询日志的server.slwlog链表
除了初始化数据结构以外,initServer还进行了一些很是重要的设置操做:
为服务器设置进程信号处理器。
建立共享对象。
打开服务器的监听端,并未监听套接字关联链接应答事件处理时,等待服务器正式运行时接收客户端链接。
为serverCron函数建立时间事件,等待服务器正式运行时,执行serverCron函数。
若是AOF持久化功能打开,那么打开现有的AOF文件,若是文件不存在则建立新的AOF文件
初始化服务器的后台I/O模块,为未来的I/O操做作准备。
执行完以上内容那么你就看到了熟悉的画面:
四、还原数据库装填
在完成对服务器状态server变量的初始化后,服务器须要载入RDB文件或者AOF文件,并根据文件记录的内容还原数据库状态
五、执行事件循环
到此服务器的初始化工做圆满完成,服务器如今开始能够接收客户端的链接请求,并处理客户端发来的命令请求。
写到这里,很是有冲动写一下.net 程序初始化以及运行过程服务端作了那些事情,着手准备!
天天学一点,总会有收获。
说明:尊重做者知识产权,文中内容参考《Redis设计与实现》,仅在此作学习与你们分享。