服务端
本章将分为三个部分:1. 服务器处理命令的过程 2. severCron函数 3. 服务器的启动过程
命令请求的执行过程
以set key value为例
- 客户端向服务端发送命令请求SET KEY VALUE
- 服务端接收并处理客户端发来的命令请求SET KEY VALUE, 在数据库中进行设置并产生命令回复OK
- 服务器将命令回复返回给客户端
- 客户端接收服务器返回的命令回复OK,并将命令回复打印给用户观看
1. 发送命令请求
Redis服务器的请求来自Redis客户端,当用户在客户端中键入一个命令请求时,客户端会将这个命令请求转化为协议格式,发送给服务器
2. 读取命令请求
当客户端与服务端之间的连接套接字因为客户端的写入而可读时,服务器将调用命令请求处理器来执行以下操作
- 读取套接字中协议格式的命令请求,将其保存到客户端状态的输入缓冲区
- 对输入缓冲区中的命令请求进行分析,提取出命令请求中包含的命令参数和命令参数的格式保存到客户端状态中,可以在客户端那一节看到
- 调用命令执行器,执行客户端指定的命令
3. 命令执行器
- 查找命令的实现
命令执行器第一件事就是根据客户端状态中命令参数的第一个参数,即“SET”的命令操作,在命令表(commandtable)中查找参数对应的命令
命令表:一个字典,键为命令名字符串,值为rediscommand结构
1 | struct redisCommand { |
标志位包括: ‘r’ 只读命令, ‘w’写入命令等等给
- 执行预备操作
在执行命令前,还有一些操作:
- 判断是否能找到对应命令
- 判断命令参数个数
- 身份验证是否通过
- …
这里不过赘述
- 命令执行
在前面我们已经得到了执行该命令的参数,该命令的实现函数,进行调用即可
1 | client -> cmd -> proc(client); |
被调用的命令实现函数会执行指定的操作,并产生相应的命令回复,这些回复都会被保存在客户端状态的输出缓冲区中。
命令执行后续操作
做一些收尾工作
- 如果服务器开启了慢查询日志功能,检查是否需要添加慢查询记录
- 更新命令执行所需要的时间
- AOF持久化功能,追加写入AOF
- 主从架构中,将该命令传播给其他从服务器
4. 将命令回复发送给客户端
命令实现函数会将命令回复保存到客户端的输出缓冲区里面,并为客户端的套接字关联命令回复处理器, 当客户端的套接字变为可写状态时,服务器就会执行命令回复处理器,将保存在客户端输出缓冲区的中命令回复发送给客户端
5. 客户端接收并打印命令回复
客户端接收命令打印命令回复
serverCron函数
serverCron函数默认每隔100ms执行一次,这个函数负责管理服务器资源,并保持服务器的良好运行
更新服务器时间缓存
Redis服务器中有不少功能需要获取系统的当前时间,而每次获取系统的当前时间都需要执行一次系统调用,为了减少系统调用的次数,对于一些对时间精度要求不是很高的场景使用时间的缓存
- 对于打印日志、更新服务器LRU时钟等等对时间精度要求不是很高的情况下采用缓存时间
- 对于为键设置过期时间,添加慢查询日志这种需要高精度时间的功能来说,服务器会再次执行系统调用而不是当前时间
更新LRU时钟
简单来说就是,因为LRU策略需要记录键被访问的时间,如果采用系统调用的方式获取时间,那么开销比较大,这里记录了系统的LRU时钟,每100ms更新,在这100ms的周期中,所有键被访问都是使用当前系统的LRU时钟
更新服务器中每秒执行命令的次数
更新服务器内存峰值的记录
处理SIGTERM信号
Redis会为服务器进程的SIGTERM信号关联处理器sigtermHandler函数,这个信号处理器负责在服务器接到SIGTERM信号时,打开服务器状态的shutdown_asap标识,当每次serverCron函数运行时,都会检查这个标识,决定是否关闭服务器(在关闭服务器前会执行RDB持久化操作)
管理客户端资源
调用clientCron函数,如果客户端连接超时则释放,如果输入缓冲区大小超过长度释放当前客户端输入缓冲区,重新创建一个默认大小的输入缓冲区
管理数据库资源
调用databaseCron函数,检查过期键等
检查持久化操作的运行状态
如果没有在执行任务持久化操作:
- BGREWRITEAOF被延迟?执行BGREWRITEAOF
- 自动保存条件是否满足?执行BGSAVE
- AOF重写条件是否满足?执行BGREWRITEAOF
将AOF缓冲区内容写入AOF文件
关闭异步客户端
增加cronloops计数器的值
可能在某些serverCron执行了几次后执行的函数中使用
初始化服务器
初始化服务器状态、接收用户指定服务器配置、创建对应的数据结构和网络连接等
1. 初始化服务器状态默认状态
创建一个redisServer的结构体并且赋值默认值,调用initServerConfig函数
2. 载入配置项
在调用initServerConfig函数初始化server变量后哦,就会开始载入用户给定的配置参数和配置文件,并根据用户设定的配置,对server变量相关属性的值进行修改
3. 初始化服务器数据结构
- clients链表
- db数组
调用initServer函数对这些数据结构初始化,同时创建了一些常量,比如回复的“OK”等等给,打开服务器的监听窗口等
4. 还原数据库状态
服务器载入RDB文件或者AOF文件吗,根据文件记录的内容来还原服务器的数据库状态。如果有AOF优先AOF,否则RDB文件
5. 开始执行事件循环
接收连接请求,执行命令