千里之行,始于足下
为了让大家对 Redis 服务有个比较直观的认知,在 底层结构 的分享前 我们会先从 服务器(redis-server)
和 客户端(redis-cli)
两个方面的概况进行简单展示,至于流程中的一些细节可能不甚了了,放心,后续我们基本都会针对性的进行详解。
这篇主要对 服务器(redis-server)
端进行讲解…
流
用户角度
即 用户操作执行流程
,以 SET KEY VALUE
操作命令为例,那么从 用户发送 操作命令 到 响应主要经历如下流程:
- 发送命令请求:客户端接收到用户输入命令,会将 命令明文 转换成 Redis 协议格式,然后通过 socket 将协议内容 发送给服务端,协议格式如
*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n
(其实也可以发现一个小规律,每个 参数 前都有一个 $n 代表该 参数 的长度,然后 \r\n 分割) - 读取命令请求:
- 读取 socket 中协议格式的命令请求,将其保存至该客户端状态的输入缓存区里
- 对输入缓冲区的命令进行解析,提取出命令请求中包含的命令参数,以及命令参数的个数,分别保存在该客户端状态的
argv / argc
中 - 调用命令执行器,执行指定命令
- 命令执行器:
- 根据
argv[0]
参数,在 服务器启动过程中构建的命令列表(command table)中查找参数指定的命令。命令表为一个字典,键为命令名称(set、get、del 等),值为一个redisCommand 结构体
,每个 redisCommand 结构体都记录着该命令的详细实现信息,其中就包括 执行函数的指针(redisCommandProc
),比如 set 对应的执行函数就是 setCommand,get 对应 getCommand - 预执行,现在我们已经有了 argc、argv、执行主体函数,但是我们还需要对一些数据进行预验证,比如 命令和参数的对等性、身份验证是否通过、内存情况 等等,当然如果在 复制状态 以及 集群状态,预执行的操作将会更多
- 调用命令的执行函数,
c->cmd->proc(c)
,调用执行逻辑后产生的响应会被保存至该 客户端状态的输出缓冲区里面(buf 属性 和 reply 属性) - 执行扫尾工作,比如 是否将这次执行记录进 慢日志、calls 计数器增加、是否执行持久化操作,如果在 复制状态下,还会有 复制操作
- 根据
- 客户端接收响应:
- 上面我们提到由 执行函数将响应保存至 客户端状态的输出缓冲区里,并为客户端的 socket 关联命令回复处理器,将缓冲区中的 响应回复给客户端,当然响应内容是协议格式,比如
+OK\r\n
- 客户端接收到 协议格式的响应,通过协议解析器把内容转换成 “明文”,显示给用户
- 上面我们提到由 执行函数将响应保存至 客户端状态的输出缓冲区里,并为客户端的 socket 关联命令回复处理器,将缓冲区中的 响应回复给客户端,当然响应内容是协议格式,比如
服务角度
即 服务端启动流程
,但是从 服务端的角度来看,其启动流程为:
- 初始化全局状态
- 载入配置
- 创建 daemon 守护进程
- 初始化服务器功能模块
- 载入持久化数据
- 开始事件循环
源码解析
由于有些方法里的代码量比较大,我们这里按照 典型的代码片段进行解析,同志们可以根据文章提示的代码位置 和 代码里面的关键词 在源码中搜素,可能数据结构一些元素 看不太懂什么意思,没关系,先混个脸熟,后面看完回头再看过来就明白了
初始化全局状态
载入配置,initServerConfig
初始化 config
初始化服务器功能模块
事件处理器主循环(重要
)
事件处理主体逻辑
- 返回处理事件数量
- 处理所有已到达的时间事件,以及所有已就绪的文件事件
- 如果不传入特殊 flags 的话,那么函数睡眠直到文件事件就绪,或者下个时间事件到达(如果有的话)
- 如果 flags 为 0 ,那么函数不作动作,直接返回
- 如果 flags 包含 AE_ALL_EVENTS ,所有类型的事件都会被处
- 如果 flags 包含 AE_FILE_EVENTS ,那么处理文件事件
- 如果 flags 包含 AE_TIME_EVENTS ,那么处理时间事
- 如果 flags 包含 AE_DONT_WAIT ,那么函数在处理完所有不许阻塞的事件之后,即刻返回
|
|
本文作者: wettper
本文链接: http://www.web-lovers.com/redis-source-server.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!