Redis服务器

目录

命令请求的执行过程

发送命令请求

读取命令请求

命令执行器(1) : 查找命令实现

命令执行器(2): 执行预备操作

命令执行器(3) : 调用命令的实现函数

命令执行器(4): 执行后续工作

将命令回复发送给客户端

客户端接收并打印命令回复

serverCron函数

更新服务器时间缓存

更新LRU时钟

更新服务器每秒执行命令次数

更新服务器内存峰值记录

处理SIGTERM信号

管理客户端资源

管理数据库资源

执行被延迟的BGREWRITEAOF

检查持久化操作的运行状态

将AOF缓冲区中的内容写入AOF文件

关闭异步客户端

增加cronloops计数器的值

初始化服务器

初始化服务器状态结构

载入配置选项

初始化服务器数据结构

还原数据库状态

执行事件循环


Redis服务器负责与多个客户端建立网络连接,处理客户端发送的命令请求,在数据库 中保存客户端执行命令所产生的数据,并通过资源管理来维持服务器自身的运转。

命令请求的执行过程

一个命令请求从发送到获得回复的过程中,客户端和服务器需要完成一系列操作。举个 例子,如果我们使用客户端执行以下命令:

redis> SET KEY VALUE OK

那么从客户端发送SET KEY VALUE命令到获得回复OK期间,客户端和服务器共需要 执行以下操作:

1)客户端向服务器发送命令请求SET KEY VALUE。

2)服务器接收并处理客户端发来的命令请求SET KEY VALUE, 在数据库中进行设置 操作,并产生命令回复OK。

3)服务器将命令回复OK发送给客户端。

4)客户端接收服务器返回的命令回复OK, 并将这个回复打印给用户观看。

发送命令请求

Redis服务器的命令请求来自Redis客户端,当用户在客户端中键人一个命令请求时, 客户端会将这个命令请求转换成协议格式,然后通过连接到服务器的套接字,将协议格式的 命令请求发送给服务器,如图

读取命令请求

当客户端与服务器之间的连接套接字因为客户端的写入而变得可读时,服务器将调用命 令请求处理器来执行以下操作:

1)读取套接字中协议格式的命令请求,并将其保存到客户端状态的输人缓冲区里面。

2)对输人缓冲区中的命令请求进行分析,提取出命令请求中包含的命令参数,以及命 令参数的个数,然后分别将参数和参数个数保存到客户端状态的argv属性和argc属性里面。

3)调用命令执行器,执行客户端指定的命令。

继续用上一个小节的SET命令为例子,图展示了程序将命令请求保存到客户端状 态的输人缓冲区之后,客户端状态的样子。

之后,分析程序将对输入缓冲区中的协议进行分析

之后,服务器将通过调用命令执行器来完成执行命令所需的余下步骤。

命令执行器(1) : 查找命令实现

命令执行器要做的第一件事就是根据客户端状态的argv[0]参数,在命令表(command table)中查找参数所指定的命令,并将找到的命令保存到客户端状态的cmd属性里面。

命令表是一个字典,字典的键是一个个命令名字,比如"set"、"get"、"del"等 等;而字典的值则是一个个redisCommand结构,每个redisCommand结构记录了一个 Redis命令的实现信息,表14-1记录了这个结构的各个主要属性的类型和作用。

表列出了sflags属性可以使用的标识值,以及这些标识的意义。

图展示了命令表的样子,并且以SET命令和GET命令作为例子,展示了redisCommand 结构:

SET命令的名字为"set", 实现函数为setCommand; 命令的参数个数为-3, 表 示命令接受三个或以上数量的参数;命令的标识为"wm"'表示SET命令是一个写 入命令,并且在执行这个命令之前,服务器应该对占用内存状况进行检查,因为这 个命令可能会占用大量内存。

GET命令的名字为"get", 实现函数为getCommand函数;命令的参数个数为2, 表示命令只接受两个参数;命令的标识为"r"'表示这是一个只读命令。

继续之前SET命令的例子,当程序以图中的argv[0]作为输人,在命令表中进 行查找时,命令表将返回"set"键所对应的redisCommand结构,客户端状态的cmd指 针会指向这个redisCommand结构,如图所示。

命令名字的大小写不影响命令表的查找结果

因为命令表使用的是大小写无关的查找算法,无论输入的命令名字是大写、小写或 者混合大小写,只要命令的名字是正确的,就能找到相应的redisCornrnand结构。比 如说,无论用户输入的命令名字是"SET"、"set"、"SeT"又或者"sEt", 命令表返 回的都是同一个redisCornrnand结构。这也是Redis客户端可以发送不同大小写的命 令,并且获得相同执行结果的原因:

命令执行器(2): 执行预备操作

到目前为止,服务器已经将执行命令所需的命令实现函数(保存在客户端状态的cmd 属性)、参数(保存在客户端状态的argv属性)、参数个数(保存在客户端状态的argc属 性)都收集齐了,但是在真正执行命令之前,程序还需要进行一些预备操作,从而确保命令 可以正确、顺利地被执行,这些操作包括:

检查客户端状态的cmd指针是否指向NULL, 如果是的话,那么说明用户输入的 命令名字找不到相应的命令实现,服务器不再执行后续步骤,并向客户端返回一个 错误。

口根据客户端cmd属性指向的redisCommand结构的arity属性,检查命令请求 所给定的参数个数是否正确,当参数个数不正确时,不再执行后续步骤,直接向客 户端返回一个错误。比如说,如果redisCommand结构的arity属性的值为-3, 那么用户输人的命令参数个数必须大于等于3个才行。

检查客户端是否已经通过了身份验证,未通过身份验证的客户端只能执行AUTH命 令,如果未通过身份验证的客户端试图执行除AUTH命令之外的其他命令,那么服 务器将向客户端返回一个错误。

如果服务器打开了maxmemory功能,那么在执行命令之前,先检查服务器的内存 占用情况,并在有需要时进行内存回收,从而使得接下来的命令可以顺利执行。如果内存回收失败,那么不再执行后续步骤,向客户端返回一个错误。

如果服务器上一次执行BGSAVE命令时出错,并且服务器打开了stop-writes­on-bgsave-error功能,而且服务器即将要执行的命令是一个写命令,那么服务 器将拒绝执行这个命令,并向客户端返回一个错误。

如果客户端当前正在用SUBSCRIBE命令订阅频道,或者正在用PSUBSCRIBE命 令订阅模式,那么服务器只会执行客户端发来的SUBSCRIBE、PSUBSCRIBE、 UNSUBSCRIBE、PUNSUBSCRIBE四个命令,其他命令都会被服务器拒绝。

如果服务器正在进行数据载入,那么客户端发送的命令必须带有1标识(比如 INFO、SHUTDOWN、PUBLISH等等)才会被服务器执行,其他命令都会被服务器 拒绝。

如果服务器因为执行Lua脚本而超时并进入阻塞状态,那么服务器只会执行客户端 发来的SHUTDO叨\f nosave命令和SCRIPT KILL命令,其他命令都会被服务器拒绝。

如果客户端正在执行事务,那么服务器只会执行客户端发来的EXEC、DISCARD、 MULTI、WATCH四个命令,其他命令都会被放进事务队列中。

如果服务器打开了监视器功能,那么服务器会将要执行的命令和参数等信息发送给 监视器。当完成了以上预备操作之后,服务器就可以开始真正执行命令了。

以上只列出了服务器在单机模式下执行命令时的检查操作,当股务器在复制或者集群模式下执行命令时,预备操作还会更多一些。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值