Redis服务器
请求命令的执行过程
客户端给服务器发送一个命令请求的过程如下:
1、客户端向服务器发送命令请求
2、服务器接受并处理客户端请求,在数据库中进行设置操作,并产生回复。
3、服务器将命令回复发送给客户端
4、客户端收到回复命令并显示给用户。
发送命令请求
客户端输入请求后,客户端会将这个请求命令转换成协议格式,然后通过连接到服务器的套接字,将协议格式发送给服务器。
读取命令请求
读取命令时,服务器会执行一下操作:
1、读取协议格式的命令,将其保存到客户端状态的输入缓冲区中。
2、对输入缓冲区的内容进行解析,提取其中包含的命令,以及命令的个数,保存到客户端状态的argv和argc的属性当中。
3、调用命令执行器,执行命令。
具体结构如下:
命令执行器(1):查找命令实现
执行器要做的第一件事就是根据客户端状态的argv[0]参数,在命令表中查找指定的命令,并将命令保存在客户端状态的cmd属性中。
一个命令表的结构如下:
设置客户端状态如下:
命令执行器(2):执行预备操作
到这里,服务器已经将客户端执行命令所需要的函数,参数以及参数个数都收集到了。但是在真正执行命令时,还需要一些预备操作:
1、检查客户端状态的cmd属性是否为null,如果为null说明输入命令错误,则不继续执行,返回错误。
2、根据cmd指向的redisCommand结构的arity属性,检查命令请求的参数个数是否正确,不正确直接返回。
3、检查客户端是否通过了身份验证。
4、如果服务器打开了maxmemory属性,那么会先检查服务器内存占用情况,并且在必要时进行内存回收。
5、如果上一次执行bgsave命令出错,并且开启了一个配置,这次命令又是一个写命令,会被直接拒绝。
6、如果客户端当前正在用SUBSCRIBE命令订阅频道,或者正在用PSUBSCRIBE命令订阅模式,那么服务器只会执行客户端发来的SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE、PUNSUBSCRIBE四个命令,其他命令都会被服务器拒绝。
7、如果服务器正在进行数据载入,那么客户端发送的命令必须带有1标识(比如INFO、SHUTDOWN、PUBLISH等等)才会被服务器执行,其他命令都会被服务器拒绝。
8、如果服务器因为执行Lua脚本而超时并进人阻塞状态,那么服务器只会执行客户端发来的SHUTDOWN nosave命令和SCRIPT KILL命令,其他命令都会被服务器拒绝。口如果客户端正在执行事务,那么服务器只会执行客户端发来的EXEC、DISCARD、MULTI、WATCH四个命令,其他命令都会被放进事务队列中。
9、如果服务器打开了监视器功能,那么服务器会将要执行的命令和参数等信息发送给监视器。当完成了以上预备操作之后,服务器就可以开始真正执行命令了。
以上只针对单机redis,如果是集群,则还要多一点。
命令执行器(3):调用命令函数实现
因为命令参数,以及个数,以及要执行的命令都已经保存,所以调用时只需要给具体函数传送一个redisClient结构体即可。
函数会执行具体的操作,然后将结果保存在客户端状态的输入缓冲区里面。
将命令回复发送给客户端
当客户端的套接字变为可写状态时,服务器就会执行命令回复处理器,将保存到客户端输出缓冲区的命令回复发送给客户端。
serverCron函数
redis的serverCron每隔100ms就会执行一次,这个函数负责管理服务器资源,并保持服务器自身运行状态良好。
更新服务器时间缓存
因为redis很多操作都会涉及到时间,而每次获取时间都需要执行一次系统调用。为了减少系统调用的次数,服务器中的unixtime和mstime被当作时间缓存:
1 | struct redisServer { |
该函数会更新服务器的时间缓存,由于每100ms更新一次,所以并不是特别准确。
更新LRU时钟
1 | struct redisServer { |
检查持久化操作运行状态
参考
《Redis设计与实现》