Redis设计与实现第14章 -- 服务器 总结(命令执行器 serverCron函数 初始化)

server/2024/11/27 8:57:51/

14.1 命令请求的执行过程

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

14.1.1 发送命令请求

当用户在客户端中输入一个命令请求的时候,客户端会把这个命令请求转换为协议格式,然后通过连接到服务器的套接字,将协议格式的命令请求发送给服务器

14.1.2 读取命令请求

当客户端与服务器之间的连接套接字因为客户端的写入而变得可读时,服务器将调用命令请求处理器来执行以下操作:
  • 读取套接字中协议格式的命令请求,并将其保存到客户端状态的输入缓冲区里面
  • 对输入缓冲区中的命令请求进行分析,提取出命令请求中包含的命令参数和个数,分别保存到argv/argc属性里面。
  • 调用命令执行器,执行客户端指定的命令

14.1.3 命令执行器1:查找命令实现

根据客户端状态的argv[0]参数,在命令表里查找参数所指定的命令,并将找到的命令保存到客户端状态的cmd属性里。

命令表是一个字典,字典的键是一个个命令名字,字典的值是一个个redisCommend结构。

主要属性有:name 名字、proc 函数指针、arity 命令参数的个数、sflags 字符串形式的标识值、flags 二进制标识、calls 服务器总共执行了多少次这个命令、millseconds 服务器执行这个命令耗费的总时长

sflags属性可以使用的标识值如下:

  • w:写入命令
  • r:只读命令
  • m:可能占用大量内存,内存紧张的话禁用
  • a:管理命令
  • p:发布订阅功能的命令
  • s:不可以在lua脚本执行
  • R:随机命令
  • S:排序
  • l:可以在服务器载入数据的过程中使用
  • t:允许从服务器在带有过期数据时使用的命令
  • M:监视器模式下不会自动被传播

14.1.4 命令执行器2:执行预备操作

+ 检查客户端状态的cmd指针是否指向NULL + 根据客户端cmd属性指向的redisCommand结构的arity属性,检查命令请求所给定的参数个数是否正确。 + 检查客户端是否通过了身份认证 + 如果服务器打开了maxmemory功能,执行命令前先检查服务器的内存占用情况,并在有需要时进行内存回收 + 如果服务器上一次执行BGSAVE命令时出错,并且服务器打开了stop-writes-on-bgsave-error功能,而且即将要执行的是写命令,那么服务器将拒绝执行 + 客户端正在用SUBSCRIBE命令订阅频道,或是正在用PSUBSCRIBE命令订阅模式,服务端只会执行客户端发来的SUBSCRIBE PSUBSCRIBE UNSUBSCRIBE PUNSUBSCRIBE 四个命令,其他命令都会被服务器拒绝 + 如果服务器正在进行数据载入,客户端发送的命令必须要带有l标识才会被执行 + 如果服务器因为执行Lua脚本而超时并进入阻塞状态,服务器只会执行客户端发来的SHUTDOWN nosave和SCRIPT KILL命令 + 如果客户端正在执行事务,只会执行EXEC DISCARD MULTI WATCH 四个命令 + 如果服务器打开了监视器功能,将要执行的命令和参数等信息发送给监视器

14.1.5 命令执行器3:调用命令实现函数

被调用的命令实现函数会执行指定的操作,并产生相应的命令回复,这些回复会被保存在客户端状态的输出缓冲区里,buf属性和reply属性。

14.1.6 命令执行器4:执行后续工作

+ 如果服务器开启了慢查询日志功能,会检查是否需要为刚刚执行完的命令请求添加一条新的慢查询日志 + 根据刚刚执行命令所耗费的时长,更新被执行命令的redisCommand结构的millseconds属性,并将命令的redisCommand结构的calls计数器的值加一 + 如果服务器开启了AOF持久化功能,会将刚刚执行的命令请求写入AOF缓冲区里 + 如果有其他从服务器正在复制当前这个服务器,那么服务器会将刚刚执行的命令传播给所有从服务器

14.1.7 将命令回复发送给客户端

命令实现函数将命令回复保存到客户端的输出缓冲区里面,并为客户端的套接字关联命令回复处理器。

当客户端套接字变成可写状态的时候,服务器就会执行命令回复处理器,将保存在客户端输出缓冲区的命令回复发送给客户端

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

客户端收到协议格式的命令回复后,会转化为人类可读的格式。

14.2 serverCron函数

每隔100ms执行一次

14.2.1 更新服务器时间缓存

Redis服务器中有不少功能需要获取系统的当前时间,而每次获取系统的当前时间都需要执行一次系统调用,为了减少系统调用的执行次数,服务器状态中的unixtime属性和mstime属性被用作当前时间的缓存

这两个属性是100ms更新一次的,所以精确度不高

  • 当打印日志、更新服务器的LRU时钟、决定是否执行持久化任务、计算服务器上线时间这种对时间精确度要求不高的功能上才会用
  • 对于为键设置过期时间、添加慢查询日志这种需要高精度时间的功能来说,服务器还是会再次执行系统调用

14.2.2 更新LRU时钟

服务器的lruclock属性保存了服务器的LRU时钟,每个redis对象都会有一个lru属性,保存了对象最后一次被命令访问的时间。当服务器计算数据库键的空转时间的时候,会用服务器的lruclock属性记录的时间减去对象lru属性记录的时间。

serverCron函数默认以10秒一次的频率更新lruclock属性的值

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

trackOperationsPerSecond函数以每100ms一次的频率执行,功能是以抽样计算的方式,估算并记录服务器在最后一秒钟处理的命令请求数量。

可以通过INFO status命令的instantaneous_ops_per_sec域查看

14.2.4 更新服务器内存峰值记录

服务器状态的stat_peak_memory属性记录了服务器的内存峰值大小

每次serverCron函数执行时,服务器都会查看当前使用的内存数量

14.2.5 处理SIGTERM信号

在启动服务器时,Redis会为服务器进程的SIGTERM信号关联处理器sigtermHandler函数,这个信号处理器负责在服务器接到SIGTERM信号时,打开服务器状态的shutdown_asap 标识

每次serverCron函数运行时,程序都会对shutdown_asap 属性进行检查,并根据属性的值决定是否关闭服务器。值为1表示关闭服务器

服务器在关闭自身前会进行RDB持久化操作,这也就是服务器拦截SIGTERM信号的原因

14.2.6 管理客户端资源

调用clientsCron函数,对一定数量的客户端进行检查:
  • 连接超时,很久没互动
  • 上一次执行请求命令后,输入缓冲区的大小超过了一定长度,程序会释放客户端当前的输入缓冲区,重新创建一个默认大小的输入缓冲区

14.2.7 管理数据库资源

databasesCron函数,对服务器中的一部分数据库进行检查,1删除过期键,收缩字典等,详情见第9章

14.2.8 执行被延迟的BGREWRITEAOF

服务器执行BGSAVE命令的期间,如果客户端向服务器发来BGREWRITEAOF命令那么服务器会将 BGREWRITEAOF命令的执行时间延迟到 BGSAVE命令执行完毕之后。服务器的aof_rewrite_scheduled标识记录了服务器是否延迟了BGREWRITEAOF命令

每次serverCron函数执行时,函数都会检查BGSAVE命令或者BGREWRITEAOF命令是否正在执行,如果这两个命令都没在执行,并且aof_rewrite_scheduled属性的值为1,那么服务器就会执行之前被推延的BGREWRITEAOF命令。

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

服务器状态使用 rdb_child_pid属性和aof_child_pid属性记录执行 BGSAVE 命令和BGREWRITEAOF命令的子进程的ID,这两个属性也可以用于检查BGSAVE命令或者BGREWRITEAOF命令是否正在执行

每次serverCron函数执行时,程序都会检查这两个属性的值,只要其中一个属性的值不为-1,程序就会执行一次wait3函数,检查子进程是否有信号发给服务器进程

  • 如果有信号到达,那么表示新的RDB文件已经生成完成或是AOF文件已经重写完毕,服务器进行后续的替换操作
  • 如果没有信号,不做动作

如果两个属性的值都是-1,说明没有进行持久化操作,检查:

  • 查看是否有BGREWRITEAOF被延迟了
  • 检查服务器的自动保存条件是否已经被满足,如果条件满足,而且服务器没有在执行其他持久化操作,那么服务器开始一次新的BGSAVE操作
  • 检查服务器设置的AOF重写条件是否满足,如果满足并且没有进行其他持久化操作,服务器开始一次新的BHREWRITEAOF操作

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

详情见11章

14.2.11 关闭异步客户端

输出缓冲区大小超出限制的客户端,详情见13章

14.2.12 增加cronloops计数器的值

cronloops属性记录了serverCron函数执行的次数,目前唯一的作用就是在复制模块中实现:每执行serverCron函数N次就执行一次指定代码的功能

14.3 初始化服务器

14.3.1 初始化服务器状态结构

创建一个redisServer类型的实例变量server作用服务器的状态,并为结构里的各个属性设置默认值,初始化函数为initServerConfig函数完成,主要工作为:设置服务器的运行ID、默认运行频率、默认配置文件路径、运行架构、默认端口号、默认RDB/AOF持久化条件、LRU时钟、创建命令表

14.3.2 载入配置选项

载入用户给定的配置参数和配置文件,并根据用户设定的配置,对server变量相关属性的值进行修改

14.3.3 初始化服务器数据结构

initServerConfig函数初始化server状态时,只创建了命令表一个数据结构,还有其他数据结构,比如:
  • server.clients链表,记录了和服务端相连的客户端的状态结构
  • server.db数组,包含了服务器的所有数据库
  • 保存频道订阅信息的pubsub_channels字典 保存模式订阅信息的pubsub_patterns链表
  • 执行lua脚本的lua环境
  • 用户保存慢查询日志的slowlog属性

这些都是在initServer函数里执行的,该函数负责初始化数据结构和设置操作,比如

  • 设置进行信号处理器
  • 设置共享对象,比如OK回复的字符串对象
  • 打开服务器的监听端口,并为监听套接字关联连接应答事件处理器,等待服务器正式运行时接受客户端的连接
  • 为serverCron函数创建时间事件,等待服务器正式运行时执行serverCron函数
  • 如果AOF持久化功能打开,创建/打开AOF文件
  • 初始化后台IO模块,bio

14.3.4 还原数据库状态

完成对服务器状态server变量的初始化之后,服务器需要载入RDB文件或AOF文件并根据文件记录的内容来还原服务器数据库状态。
  • 如果开启了AOF持久化功能,用AOF文件还原
  • 否则,用RDB文件还原

14.3.5 执行事件循环


http://www.ppmy.cn/server/145299.html

相关文章

大数据新视界 -- 大数据大厂之 Hive 数据桶:优化聚合查询的有效手段(下)(10/ 30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

刷题日常(移动零,盛最多水的容器,三数之和,无重复字符的最长子串)

移动零 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 俩种情况: 1.当nums[i]为0的时候 直接i 2.当nums[i]不为0的时候 此时 …

Java面试之多线程并发篇

前言 本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!说一说自己对于 synchronized 关键字的了解?说说自己是怎么使用 synchronized 关键字?什么是线程安全?Vector是一个线程安全类吗?…

《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试

一、论文概述 基于FPGA的便携式PWM方波信号发生器是一篇由任青颖、庹忠曜、黄洵桢、李智禺和张贤宇 等人发表的一篇期刊论文。该论文主要研究了一种新型的信号发生器,旨在解决传统PWM信号发生器在移动设备信号调控中存在的精准度低和便携性差的问题 。其基于现场可编…

NeurIPS 2024 有效投稿达 15,671 篇,数据集版块内容丰富

NeurIPS,全称 Neural Information Processing Systems Conference,是神经信息处理系统的年度学术会议。该会议始于 1987 年,当时名为 NIPS。随着人工智能领域的快速发展,其影响力逐渐扩大,被越来越多的研究者和企业关注…

解决Flink读取kafka主题数据无报错无数据打印的重大发现(问题已解决)

亦菲、彦祖们,今天使用idea开发的时候,运行flink程序(读取kafka主题数据)的时候,发现操作台什么数据都没有只有满屏红色日志输出,关键干嘛?一点报错都没有,一开始我觉得应该执行程序…

PAT甲级-1134 Vertex Cover

题目 题目大意 给定一个图,n是定点数,m是边数,给出每条边的两个顶点来表示边。又给定k个顶点集,要求判断这些顶点集是否是定点覆盖集。是的话输出Yes,否则输出No。 思路 vertex cover是顶点覆盖的意思,即…

Web开发技术栈选择指南

互联网时代的蓬勃发展,让越来越多人投身软件开发领域。面对前端和后端的选择,很多初学者往往陷入迷茫。让我们一起深入了解这两个领域的特点,帮助你做出最适合自己的选择。 在互联网发展的早期,前端开发主要负责页面布局和简单的…