Redis——第13,14章:redisClient + redisServer + 交互过程 + serverCron函数

  • redisClient
    • 结构
    • 创建与关闭
    • 伪客户端
  • redisServer
    • 结构
    • 初始化
  • Client和Server交互过程
  • serverCron时间事件函数(12件事)

1.redisClient

(1)结构

是否伪客户端int fd;记录客户端正在使用的socket描述符fd=-1:伪客户端,AOF载入或Lua脚本
fd>-1:普通客户端
client自身属性robj *name;客户端名字CLIENT setname命令可以更改name
int flags;属性标志记录客户端的role(状态)eg. REDIS_MASTER | REDIS_MULTI:该客户端是一个主服务器并且正在执行事务
time_t ctime;
time_t lastinteraction;
time_t obuf_soft_limit_reached_time;
记录了创建客户端的时间;
记录了客户端与服务器最后一次互动时间;
记录了输出缓冲区第一次到达软性限制的时间
用于计算age:连接了多少秒
用于计算空转时间
用于缓冲区大小限制
与Server交互相关sds querybuf;输入缓冲区,用于保存客户端发送的命令请求必须<1GB,不然将自动关闭客户端
eg.如果客户端向服务器发送SET key value。则会缓冲区将包含这个内容
robj **argv;
int argc;
服务器对缓冲区内容分析之后,
会将命令参数及对应个数存在argv和argc中
eg. SET key value
argv[0]="SET" argv[1]="key" argv[2]="value"
argc=3 共三个参数
struct redisCommand *cmd;服务器根据argv和argc查找对应的实现函数,保存在redisCommand命令表中将cmd指向命令表,根据这个查找对应的实现函数
char buf[REDIS_REPLY_CHUNK_BYTES];
int bufpos;
固定大小的输出缓冲区用于保存长度比较小的回复,比如OK,整数值,回复错误等默认REDIS_REPLY_CHUNK_BYTES = 16KB
bufpos用于记录已经使用的字节数量
list *reply;可变大小的缓冲区用于保存长度比较大的回复,
比如长字符串,集合等
链表形式,每个链接节点存储一个StringObject
身份验证struct redisCommand *cmd;身份验证,=0表示未通过身份验证,=1表示验证成功如果开启身份验证:=0时,所有命令都会被拒绝。未开启身份验证:该属性无作用
AUTH用于验证命令
身份信息可以通过conf中requirepass配置
typedef struct redisClient{
 
    //记录客户端正在使用的socket描述符
    int fd;

    //客户端名字
    robj *name;

    //属性标志记录客户端的角色
    int flags;

    //输入缓冲区,用于保存客户端发送的命令请求
    sds querybuf;

    //服务器对缓冲区内容分析之后,会将命令参数及对应个数存在argv和argc中
    robj **argv;
    int argc;

    //服务器根据argv和argc查找对应的实现函数,保存在redisCommand命令表中
    struct redisCommand *cmd;

    //两个输出缓冲区
    //固定大小的缓冲区用于保存长度比较小的回复,比如OK,整数值,回复错误等
    char buf[REDIS_REPLY_CHUNK_BYTES];
    int bufpos;

    //可变大小的缓冲区用于保存长度比较大的回复,比如长字符串,集合等
    //链表形使,每个链接节点存储一个StringObject
    list *reply;

    //身份验证,=0表示未通过身份验证,=1表示验证成功
    int authenticated;

    //记录了创建客户端的时间,用于计算age:连接了多少秒
    time_t ctime;
    //记录了客户端与服务器最后一次互动时间,用于计算空转时间
    time_t lastinteraction;
    //记录了输出缓冲区第一次到达软性限制的时间,用于缓冲区大小限制
    time_t obuf_soft_limit_reached_time;
    
}redisClient;

(2)创建与关闭

  • 创建:新Client添加到RedisServer的链表末尾
  • 关闭:有多种原因都会引起Client关闭
    • 客户端退出或者被杀死
    • 客户端向服务端发送了不符合协议格式的命令
    • timeout:如果配置了timeout属性,空转时间>timeout。(当然也有例外,主从模式,订阅等情况)
    • 客户端发送的命令请求超过输入缓冲区大小(默认1GB)
    • 客户端接受的回复超过输出缓冲区大小

(3)伪客户端

Lua脚本AOF文件载入都会创造伪客户端

2.redisServer

(1)结构

与redisDb相关redisDb *Db;一个数组保存服务起中所有数据库链表形式
int dbnum;记录数据库大小,默认16 
与redisClient相关list *clients;数组保存所有Client客户端链表形式,新加入的客户端加入链表尾
自动RDB相关struct saveparam *saveparams;记录了保存条件的数组与conf配置中的save相关
long long dirty;修改计数器记录距离上次RDB到现在一共更改了多少次
time_t lastsave;上一次执行保存的时间 
AOF相关sds aof_buf;AOF缓冲区 
int aof_rewrite_scheduled;记录服务器中 BGREWRITEAOF 命令执行是否被延迟当=1时,会将 BGREWRITEAOF 命令延迟到 BGSAVE 命令执行成功后再执行
BGSAVE和BG重写

pid_t rdb_child_pid;

pid_t aof_child_pid;

记录执行 BGSAVE的子进程ID

记录执行BGREWRITEAOF的子进程ID

都=-1:表示目前没有执行相关持久化

有一个!=-1:表示后台在持久化,需要实时检测是否完成和执行后续操作(比如替换原有RDB文件,AOF重写追加等)

当前时间戳time_t unixtime;秒级精度的系统当前UNIX时间戳 
long long mstime;毫秒级精度的系统当前UNIX时间戳 
内存size_t stat_peak_memory;更新内存峰值记录从开始到现在的最大峰值,每次serverCron执行都会更新
空转时长unsigned lruclock:22;

记录了服务器的LRU时钟。serverCron 以每 10 秒一次的频率更新 lruclock 属性的值。

LRU 时钟不是实时的,它只是一个模糊的估计值。

拿该值减去redisObject的lru属性,就是空转时间
serverCron执行次数int cronloops;记录serverCron函数执行的次数

唯一作用是:在复制模块中实现“每执行serverCron函数N次就执行执行代码”

if(cronloops % N == 0){代码....}

struct redisServer{
 
    //AOF相关
    //AOF缓冲区
    sds aof_buf;
    
    //RDB相关
    //记录了保存条件的数组
    struct saveparam *saveparams;    
    //修改计数器
    long long dirty;
    //上一次执行保存的时间
    time_t lastsave;

    //一个链表,保存了所有客户端状态
    list *clients;

    //秒级精度的系统当前UNIX时间戳
    time_t unixtime;
    //毫秒级精度的系统当前UNIX时间戳
    long long mstime;

    //默认每10s更新一次的时钟缓存
    //用于计算key的空转时长
    unsigned lruclock:22;

    //已使用内存峰值
    size_t stat_peak_memory;

    //用于延迟执行重写aof
    int aof_rewrite_scheduled;
}

(2)初始化

初始化的工作由initServerConfig函数完成

  1. 初始化状态结构:设置运行ID,运行频率,conf路径,默认端口号,是否RDB/AOF,LRU时钟,创建命令表
  2. 载入配置:更改配置
  3. 初始化其他
    1. 维护clients链表,db数组,频道订阅,慢查询日志等
    2. 设置信号处理器,创建共享对象(如1到10000的字符串对象),运行serverCron函数,初始化I/O模块
  4. 根据RDB或者AOF还原数据库
  5. 执行事件循环(loop)

3.服务器与客户端的交互

以客服端执行 set key value指令为例,描述整个交互流程

  • 客户端发送命令
    • 这个命令将被转换成协议发送给服务器,格式如下
  • 服务器读取命令
    • 根据socket和协议,将其保存到客户端中的querybuf输入缓冲区
    • 分析querybuf中的命令,解析完成后保存在客户端中的argv和argc
    • 准备执行该指令
  • 命令执行器1:查找命令实现
    • 根据argv[0]的参数(eg. SET),在命令表中查找命令,并保存到客户端的cmd属性中
  • 命令执行器2:执行预备校验
    • 目前为止,服务器准备工作已经完成(cmd记录了实现函数地址,argv保存了参数,argc保存了参数个数)
    • 执行前需要进行检查:cmd指向的实现函数是否为null,参数个数是否正确,身份验证是否正确等...
  • 命令执行器3:调用实现函数
    • 服务器执行实现函数。将回复保存到redisClient的输出缓冲区中(buf和reply)
  • 命令执行器4:执行后续工作
    • 如果执行太慢,假如慢查询日志;如果开启了AOF,则写入AOF缓冲;如果其他服务器正在复制,则发送到其他服务器
  • 将结果返回给客户端
    • 客户端接受回复命令并打印,服务器清空客户端的输出缓冲区

4.serverCron函数

该函数每100毫秒执行一次,执行一次需要做12件事

  • 更新服务器时间缓存:unixtime和mstime
    • 这两个属性用于对精度要求不高的功能:如 打印日志,更新服务器LRU时间,决定是否持久化,计算服务器上线时间
    • 对于过期时间,添加慢查询等要求高的功能,服务器会额外执行系统调用保证精准时间
  • 更新lruclock
    • 该属性记录了服务器的LRU时间(上次被使用的时间戳),默认每10秒更新一次,是一个模糊的估算值
    • 计算redisObject空转时间时就用 redisServer.lruclock属性 - redisObject.lru属性 得出空转时间
  • 更新服务器内存峰值:stat_peak_memory属性(最大使用情况)
    • 每次serverCron函数执行时,程序都会查看服务器当前内存使用情况,如果>stat_peak_memory,则替换
  • 更新服务器每秒执行命令数
    • serverCron中的trackOperationsPerSecond函数
    • 每100毫秒执行一次,通过抽样方式来估算 服务器在最近一秒内处理的命令数量
  • 处理sigterm信号
  • 管理客户端资源
    • serverCron每次都对一定数量的客户端进行检查
    • 客户端很长时间没有和服务器响应(>timeout),服务器认为该客户端超时,则会断开和该客户端的连接。
    • 当客户端在上一次执行命令请求后,输入缓冲区超过规定的长度,程序会释放输入缓冲区,并创建一个默认大小的缓冲区,防止缓冲区过分消耗。
    • 关闭输出缓冲区超出大小限制的客户端。
  • 管理数据库资源
    • 主要是检查键是否过期,并且按照配置的策略,删除过期的键。如懒惰删除、定期删除等。
  • 执行被延迟的bgrewriteaof命令
    • 属性aof_rewrite_scheduled记录是否有延迟的bgrewriteaof命令。 当执行bgsave命令期间,如果接收到bgrewriteaof命令,不会立即执行该命令,而是会将属性aof_rewrite_scheduled置成1。每次执行serverCron函数执行时,发现属性aof_rewrite_scheduled是1,会检查当前是否在执行bgsave命令或bgrewriteaof命令,如果没有在执行这两个命令,则会执行bgrewriteaof命令
  • 检查持久化操作的运行状态
    • 检查rbd_child_pid和aof_child_pid的值
    • 都为-1:目前没有执行持久化
      • (1)如果存在BGREWRITEAOF延迟,执行BGREWRITEAOF
      • (2)如果save配置中自动保存RDB满足,执行BGSAVE
      • (3)AOF重写的条件是否满足?执行BGREWRITEAOF:不做动作
  • 将aof缓冲区内容写入osBuffer
    • 如果开启aof,redis每次写命令都会记录到aof缓冲区中
    • serverCron每100毫秒都会将缓冲区中的内容写入osbuffer中
    • 每次loop会根据appendfsync的策略将osbuffer写入到文件中
  • 关闭异步客户端
    • 关闭输出缓冲区超出限制的客户端
  • cronloops++
    • redis用属性cronloops保存serverCron函数执行的次数。当执行一次serverCron,则会将属性值加1。
    • 这个值目前的唯一作用,在复制模块中实现“每执行serverCron函数N次就执行执行代码”
      • if(cronloops % N == 0){自定义代码....}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值