一.客户端与服务端交互
本篇简单介绍下服务器,服务器运行涉及的内部原理知识很多,主要了解Redis服务器内部要做哪些事情,需要开发人员去干预的比较少。Redis服务器负责与多个客户端建立网络连接,处理客户端发送的命令请求,在数据库中保存客户端执行命令所产生的数据,并通过资源管理来维持服务器自身的运转。本节先说客户端与服务器交互原理:服务器与客户端进行了什么交互,服务器中的各个不同组件又是如何协作的。在详细了解客户端与服务器在执行命令请求时所做的各种工作之前,先慨括看下命令请求的执行步骤过程:
(1) 客户端向服务器发送命令请求,比如set key value 。
(2) 服务器接收并处理客户端发来的命令请求,在数据库中进行设置操作,并产生命令回复OK。
(3) 服务器将命令回复OK发送给客户端。
(4) 客户端接收服务器返回的命令回复OK,并打印给用户看。
1.1 客户端发送命令请求
Redis服务器的命令请求来自Redis客户端,当用户在客户端中输入一个命令请求时,客户端会将这个命令请求转换成协议格式,然后通过连接到服务器的套接字,将协议格式的命令请求发送给服务器。
1.2 服务端读取命令
当客户端与服务器之间的连接套接字因为客户端的写入而变得可读时,服务器将调用命令请求处理器来执行以下操作:
(1) 读取套接字中协议格式的命令请求,并将其保存到客户端状态的输入缓冲区里面。
(2) 对输入缓冲区中的命令请求进行分析,提取出命令请求中包含的命令参数,以及命令参数的个数。分别保存到服务端记录客户端状态的argv和argc属性中。
(3) 调用命令执行器,执行客户端指定的命令。
1.3 命令执行器
(1) 查找命令实现
命令执行器要做的第一件事就是根据客户端状态的argv[0]参数,在命令表(command table)中查找参数所指定的命令,并将找到的命令保存到客户端状态的cmd属性里面。命令表是一个字典,字典的键是一个个命令的名字,比如常见的如: get , set ,del等命令,字典的值则是一个个redisCommand结构,每个结构记录了一个命令的实现信息。redisCommand结构的主要属性就不在此了解。
(2) 执行预备操作
服务器已经将执行命令所需的命令实现函数(客户端状态的cmd属性),参数(客户端状态的argv属性),参数个数(客户端状态的argc属性)都收集全了,但在真正执行命令之前,程序还要进行一些预备操作,确保命令可以正确,顺利地被执行。简单说包括:1.检查cmd属性中命令是否正确。2.参数以及参数个数是否正确。3.是否通过身份验证。4.如果打开了maxmemory功能,那么在执行命令之前,先检查服务器内存占用情况,需要时进行内存回收,以接下来的命令可以顺利执行。5.如果服务器正在进行数据载入,那么客户端发送的命令会被服务器拒绝。6.如果客户端当前正使用subscribe命令订阅频道,或者用psubscribe命令订阅模式,那么其它命令都会被服务器拒绝,8.如果客户端正在执行事务,那么服务器只会执行客户端发来的exec,discard,multi,watch命令,其它命令都会被放进事务队列中。9如果服务器开启了监视器功能,那么服务器会将要执行的命令和参数信息发送给监视器。10.如果服务器因为执行Lua脚本而超时并进入阻塞状态,那么其他命令会被服务器拒绝。注意:如果服务器是在复制或者集群模式下,预备操作会更多。完成了以上预备操作之后,服务器才会执行命令。
(3) 执行命令实现函数操作
服务器已经将要执行命令的实现保存到了客户端状态的cmd属性里,并将命令的参数和参数个数分别保存到了客户端状态的argv属性和argv属性中,当服务器决要执行命令时,内部只要执行以下语句就可以了:
//clinet是指向客户端状态的指针 client->cmd->proc(client);
当执行命令操作后,会产生相应的命令回复,比如ok, 这些回复会被保存在客户端状态的输出缓冲区里面(redisClient结构的buf属性和reply属性),之后还会为客户端的套接字关联命令回复处理器,这个处理器命令回复返回给客户端。
(4)执行后续工作
当执行命令实现函数之后,服务器还需要执行后续工作:1.如果服务器开启了慢查询日志功能,那么慢查询日志模块会添加一条新的慢查询日志。2.根据执行命令所耗时的时长,更新被执行命令的redisCommand结构的millisecondes属性,并将命令的redisCommand结构的calls计数器值增一。3.如果开启了AOF功能,刚执行的命令请求写入到AOF缓冲区中。4.如果有从服务器正在复制,那么该命令会传播给所有从服务器。
当以上操作都执行完了后,服务器就可以继续从文件事件处理器中取出并处理下一个命令请求了。
二. serverCron函数
在上节中介绍了客户端与服务端交互过程,这节了解serverCron函数执行操作,并说明这些操作对于服务器维持正常运行有何帮助。 redis服务器中的serverCron函数默认每隔100毫秒执行一次,负责管理服务器的资源,并保存执行器自身的良好运转。
2.1 更新服务器时间缓存
redis服务器中有不少功能需要获取系统当前时间,每次获取系统的当前时间都需要执行一次系统调用,为了减少系统调用的执行次数,服务器状态redisServer结构的unixtime属性和mstime属性被用作当前时间的缓存。默认每隔100毫秒一次频率更新unixtime属性和mstime属性,所以这两个属性记录的时间的精确度并不高。一般用在服务器打印日志、更新服务器的LRU时钟、决定是否执行持久化任务、计算服务器上线的时间(uptime)等这类对时间精度度要求不高的功能上。对于要求精确度高的时间,会再次执行系统调用获取,一般用在为键设置过期时间、添加慢日志等功能上。
2.2 更新LRU时钟
LRU全称是Least Recently Used,即近期最少使用算法。用于内存数据清除方面,在第15篇中有介绍。服务器状态redisServer结构的lruclock属性保存了服务器的LRU时钟。默认每隔10秒更新一次时钟缓存。通过该算法计算一个数据库键的空转时间。
127.0.0.1:6379> set msg "hello" OK 127.0.0.1:6379> object idletime msg (integer) 8 127.0.0.1:6379> object idletime msg (integer) 14
在Redis4.0版本中,感觉这个空转时钟很精确,不像默认10秒一次更新lurclock属性的值。
原文地址:https://www.cnblogs.com/lonelyxmas/p/10230773.html