Redis(十八):服务器

本文详细阐述了Redis命令请求的执行过程,从客户端发送命令请求到服务器接收、解析、执行命令,再到返回命令回复。重点介绍了服务器如何查找命令实现、执行预备操作,以及调用命令实现函数。同时,提到了在执行过程中涉及的内存管理、权限验证、命令过滤等关键步骤,最后讨论了执行后续工作,如慢查询日志、AOF持久化和命令传播等。


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

命令请求的执行过程

一个命令请求从发送到获得回复的过程中,客户端与服务器需要完成一系列的过程

  • 客户端向服务器发送命令请求
  • 服务器接收并且处理客户端发来的命令请求,并且产生命令回复
  • 服务器将命令回复发送给客户端
  • 客户端接收服务器的命令回复

下面对这4个步骤进行详解

发送命令请求

用户首先要进行输入命令,然后客户端要将这条命令转化成协议格式,然后客户端连接到服务器的套接字,将协议格式的命令请求发送给服务器。

过程如下图所示
在这里插入图片描述

读取命令请求

客户端将协议格式发送给服务端,此时是发送在连接的套接字那里,然后套接字就会产生AE_READABLE事件(表示可读状态),然后就会调用命令处理器来执行以下操作

  • 读取套接字中协议格式的命令请求,并将其保存到客户端状态(RedisClient)的输入缓冲区里面,即querybuf属性,是一个SDS类型的变量(回看客户端的文章)
  • 对输入缓冲区中的命令请求进行分析(因为是协议格式,所以要进行分析),提出命令请求中的所有命令参数,以及统计命令参数个数,然后将参数分别保存到客户端状态里面的argv属性和argc属性
  • 服务器调用命令执行器,服务器执行客户端指定的命令。
命令执行器的步骤

命令执行器再拿到argv数组时,要进行下列的步骤

  • 查找命令的实现
  • 执行预备操作
  • 调用命令的实现函数
  • 执行后续工作
查找命令的实现

argv数组里面,第一个元素保存的就是命令的类型(set、get、zadd等命令),在RedisServer(服务器状态)里面保存有一个名为cmd属性,cmd属性是字典类型的,key就是这些命令名字构成的字符串对象,Value则是对应的RedisCommand结构,该结构是记录了命令的具体实现的。

RedisCommand的结构
属性名类型作用
namechar *命令的名字,比如set
arityint命令参数的个数,用来检查命令请求的格式是否正确。对于可以给多个参数的命令,该属性可能为-N,表示参数的数量大于等于N。要注意的一点是,命令参数的个数要包含命令的名字,比如set key value,set命令的参数个数就是3个
procredisCommandProc*是一个函数指针,指向命令的实现函数
sflagschar *是一个标识值,标识该命令是读还是写命令,并且可以标识该命令可不可以在lua脚本使用,是一个字符串
flagsint对sflags标识进行分析得出的二进制标识,由程序自动生成的,服务器校验时都是根据flags属性的,而不是根据sflags属性,因为flags属性是一个整形,可以通过一些二进制运算来进行分析判断
callslong long服务器总共执行了多少次这个命令
millisecondslong long服务器执行这个命令所耗费的总时长

slfags属性的标识

标识意义带有该标识的命令
w写入命令SET RPUSH DEL等
r只读命令GET STRLEN EXISTS等
m可能会占用大量内存,运行时要检查内存是否足够LPUSH SADD等一些O(N)复杂度的命令
a管理命令SAVE ;BGSAVE;SHUTDOWN等
p发布订阅功能方面的命令PUBLIC SUBSCRIBE等
s该命令不可以在Lua脚本中使用
R随机命令,即相同的命令,返回值可能不同SPOP,RANDOMKEY等
S当在Lua脚本中使用该命令时,对这个命令的输出结果进行一次排序,让命令的结果有序
I命令可以在服务器载入数据时使用
t该命令允许从服务器在带有过期数据时使用
M这个命令在监视器模式下不会自动被传播
大小写不会影响查找命令的结果集

命令表使用的是大小写无关的查找算法,无论输入的命令还是大写、小写或者混合大小写,只要名字是正确的,就可以找到相应的RedisCommand

执行预备操作

此时,服务器已经将执行命令所需的命令实现函数(cmd属性)、命令参数与命令参数个数(argv,argc)都已经收集齐了,但是此时并不是立即开始执行操作,程序还会进行一些预备操作,从而确保命令可以正确、顺利地被执行,这些命令包括以下

  • 检查客户端是否已经通过验证,即看客户端的里面有一个名为authenticated的属性,如果为1,代表已经验证,不为1,就代表未验证,返回一个错误

  • 检查客户端的cmd指针是否为NULL,即是否找到了对应的实现函数,如果为NULL,代表命令输入有误,那么服务器并不会去执行,并且向客户端返回一个错误

  • 经过上个判断,cmd指针不为NULL,也就是命令是正确的,此时要判断参数是否合理,通过RedisCommand结构的arity属性和argc属性进行比较,如果参数个数不正确,也是不会去执行,并且向客户端返回错误,注意arity属性是可以为负数的,如果为-N,就看argc是否大于等于N

  • 最后一步是检查是否打开了maxmemory功能,如果打开了,服务器会先查看当前的内存使用情况,并且会在有需要时进行内存回收,让实现命令的时候可以顺利执行,如果内存不够且回收失败,就不再执行后续步骤,向客户端返回错误

  • 在配置文件有一个选项为stop-writes-on-bgsave-error,如果该选项为yes,那么就代表着如果RDB异步持久化失败,就不会执行写操作,所以,当上一条命令为BGSAVE时且执行失败后,如果下一条还是写命令就不会执行,服务端拒绝执行操作

  • 如果客户端当前正在用SUBSCRIBE命令订阅频道,或者使用PSUBSCRIBE命令订阅模式,那么服务器对于这个客户端只会执行SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE、PUNSUBSCRIBE,这4个命令(第一个是订阅频道,第二个是订阅指定模式的频道,第三个是退订指定频道,第四个是退订指定模式的频道),其他命令都会被拒绝

  • 如果服务器此时正在进行数据载入,那么slags属性必须为I,否则不会执行

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

  • 如果客户端正在执行事务,那么服务器只会执行客户端发来的EXEC、DISCARD、MULTI、WATCH命令这4个命令(这4个命令都是事务相关命令,MULTI开启事务,DISCARD取消事务,EXEC事务结束,WATCH在事务中监视指定Key,如果key被修改,事务被打断,会回滚),其他命令都会被放进事务队列中

  • 如果服务器打开了监视器功能,那么服务器会将要执行的命令和参数等信息发送给监视器

调用命令的实现函数

进行了上面一大串的预备操作,下面可以正式开始执行操作

客户端要执行的命令实现已经保存到cmd属性里面,并将命令的参数和参数个数分别保存到了客户端状态的argv属性和argc属性中,当服务器决定要执行命令,只需要执行cmd里面的RedisCommand里面的proc属性

client->cmd->proc(client) //将整个客户端状态作为参数传给proc

被调用的命令实现函数会执行指定的操作,并且会产生相应的命令回复,将这些回复保存到RedisClinet的buf和reply属性中去,之后还会为客户端的套接字关联命令回复处理器(关联AE_WRITEABLE事件),之后客户端请求读取回复时是要用这个处理器将命令回复返回给客户端

执行后续工作

执行完实现函数之后,服务器还需要进行一些后续工作

  • 如果服务器开启了慢查询日志功能,那么服务器的慢查询日志模块会检查是否需要为刚刚执行完的命令请求去添加一条慢查询日记记录
  • 计算出执行命令所耗费的时长,更新RedisCommand结构里面的millseconds属性,并且让calls属性进行自增1(该RedisCommand不是该客户端独有的,而是全局的)
  • 如果服务器开启了AOF持久化功能,那么AOF持久化模块会将执行的命令请求写入到AOF文件中,进行持久化
  • 如果有其他从服务器正在复制当前这个服务器,那么服务器会将刚刚执行的命令传播给所有的从服务器
将命令回复发送给客户端

执行完后续工作后,就可以进行回复了,但回复也是要客户端先进行请求,服务器才会进行回复

命令实现函数将回复信息保存在客户端状态里面的buf或者reply属性中,并且为客户端的套接字绑定了命令回复处理器,关联的是AE_WRITEABLE事件,此时用户连接套接字并且请求回复,发产生AE_WRITEABLE属性,该事件就会被窃听到,然后就会调用命令回复处理器,将客户端状态里面的回复发送给客户端

回复完后,命令回复处理器还会将该客户端状态里面的buf和reply清空,并且解除套接字与回复命令套接字的关联,为下一次的命令请求做好准备。

这里发送的回复,也是按照协议格式进行发送的

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

当客户端那边接收到回复后,会将协议格式的回复进行转换,转换成可读形式,并且打印给用户去观看。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值