聊聊Redis的单线程模型
作者:nimo10050 发布时间:2022-02-21 09:20:42
开篇
本文主要来探讨一下 redis 的单线程模型,文章前半部分会先引用某网络课程讲解的内容(图片+语言描述),后半部分是本人粗略阅读 redis 源码后整理出来的一份伪代码,用来验证文中前半部分的内容。
本文对标的 redis 版本是 5.x。
redis 涉及的知识点有很多,展开来讲能聊到操作系统,因此为了方便理解,文中做了很多抽象描述。
文件事件处理器
redis 内部使用了一个叫 文件事件处理器( file event handler)的东西,这个文件事件处理器是 单线程 的,所以才有了 redis 是单线程的这一说法。
文件事件处理器的结构如下图:
它包含 4 个部分:
多个 socketIO
多路复用程序
文件事件分派器
事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
文件事件处理器 采用 IO 多路复用机制 同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序 会监听多个 socket,会将 socket 产生的事件放入 队列 中排队,事件分派器 每次从队列中取出一个事件,把该事件交给对应的 事件处理器 进行处理。
来看 redis 客户端与服务端的一次通信过程:
1. 接收连接请求:
客户端 socket01 向 redis 的 server socket 请求建立连接,此时 server socket 会产生一个 AE_READABLE 事件,
IO 多路复用程序监听到 server socket 产生的事件后,将该事件压入队列中。
文件事件分派器从队列中获取该事件,交给连接应答处理器。
连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 AE_READABLE 事件与 命令请求处理器 关联。
2. 读取请求内容:
假设此时客户端发送了一个 set key value 请求,此时 redis 中的 socket01 会产生 AE_READABLE 事件
IO 多路复用程序将事件压入队列
事件分派器从队列中获取到该事件,由于前面 socket01 的 AE_READABLE 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。
命令请求处理器读取 socket01 的 key value 并在自己内存中完成 key value 的设置。操作完成后,它会将 socket01 的 AE_WRITABLE 事件与命令回复处理器关联。
3. 回复请求:
如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 AE_WRITABLE 事件,同样压入队列中,
事件分派器找到相关联的命令回复处理器
由命令回复处理器对 socket01 输入本次操作的一个结果,比如 ok,之后解除 socket01 的 AE_WRITABLE 事件与命令回复处理器的关联。
这样便完成了一次通信。
redis 事件处理伪代码
// 入口函数
void aeMain(EventLoop eventLoop) {
while(true) {
// 文件事件处理器
aeProcessEvents(eventLoop);
}
}
void aeProcessEvents() {
// 调用 epoll_wait 函数,等待I/O事件 (IO 多路复用程序)
int numevents = aeApiPoll(timeval);
for(int i=0; i< numevents; i++) {
// 从队列中取出对应的事件
fileEvent = getFromEventQueue(i);
// 处理文件事件(文件事件分派器)
processFileEvent(fileEvent);
// 处理时间事件。(忽略)
processTimeEvent();
}
}
void processFileEvent() {
if event == '读事件' {
// 读处理器
processReadFile();
}
if event == '写事件' {
// 写处理器
processWriteFile();
}
}
redis 源码
篇幅原因这里就不贴 redis 的源代码,可以用 vscode 等工具打开 redis 安装目录下的 src 目录,通过全局搜索 aeMain 找到入口。
来源:https://blog.csdn.net/cnm10050/article/details/110069606


猜你喜欢
- 初次使用IDEA,创建一个maven工程,发现在目录结构中产生了两个不一样的东西——.iml文件和.idea文件夹。非常好奇,所以立刻上网查
- 本文实例讲述了C#接口在派生类和外部类中的调用方法。分享给大家供大家参考,具体如下:C#的接口通过interface关键字进行创建,在接口中
- REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系
- 前言有些朋友可能是从事开发工作的时间不是特别的长,所以觉得Service相对与另外两个组件activity、broadcast receiv
- 一. 接口文档概述swagger是当下比较流行的实时接口文文档生成工具。接口文档是当前前后端分离项目中必不可少的工具,在前后端开发之前,后端
- 本文以实例形式介绍了基于Java实现的Dijkstra算法,相信对于读者研究学习数据结构域算法有一定的帮助。Dijkstra提出按各顶点与源
- 上篇博文:Java-多线程的使用equals与==1. ==的使用1.1 概述⭕ 用于基本类型比较值时:只要两个变量的值相等,即为true。
- 用户可以自定义打印某一年的年历,即:把某一年的日历全部打印出来如把2013年的年历打印出来如下:January 2013&nbs
- 引言前一段有幸参与到一个智能家居项目的开发,由于之前都没有过这方面的开发经验,所以对智能硬件的开发模式和技术栈都颇为好奇。智能可燃气体报警器
- 前言我们知道在 Java 中读取一些配置文件信息,是在开发中十分常用的要求。例如:这里我们使用 JDBC 实例:连接My
- 在阅读本文之前,大家可先参阅《简单理解Spring之IOC和AOP及代码示例》一文,了解下Spring中IOC和AOP的相关内容。下面进入正
- 1 运算符1.1 概述运算符 用于连接 表达式 的 操作数,并对操作数执行运算。例如,表达式num1+num2,其操作数是num1和num2
- 本文借由并发环境下使用线程不安全的SimpleDateFormat优化案例,帮助大家理解ThreadLocal.最近整理公司项目,发现不少写
- 队列在编程语言中是如何定义的呢?小编与大家分享自己的经验。队列的定义队列是限制结点插入操作固定在一端进行,而结点的删除操作固定在另一端进行的
- 这篇文章主要介绍了Java使用Collections工具类对List集合进行排序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一
- 示例代码:<%@ Page Language="C#" AutoEventWireup="true&qu
- Java 堆是用来存储对象实例的, 因此如果我们不断地创建对象, 并且保证 GC Root 和创建的对象之间有可达路径以免对象被垃圾回收,
- 本文研究的主要是SpringMVC中使用Thymeleaf模板引擎的相关内容,具体介绍如下。Thymeleaf提供了一组Spring集成,允
- Maven 的生命周期maven 将项目的生命周期(Lifecycle)抽象为了三种,每种生命周期中又包含了多个阶段(Phase)。也就是说
- 这篇文章主要介绍了Mybatis 缓存原理及失效情况解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的