白癜风患者的就医误区 http://m.39.net/baidianfeng/a_5212353.html开篇
本文主要来探讨一下redis的单线程模型,文章前半部分会先引用某网络课程讲解的内容(图片+语言描述),后半部分是本人粗略阅读redis源码后整理出来的一份伪代码,用来验证文中前半部分的内容。
本文对标的redis版本是5.x。
正文
redis涉及的知识点有很多,展开来讲能聊到操作系统,因此为了方便理解,文中做了很多抽象描述。
文件事件处理器
redis内部使用了一个叫文件事件处理器(fileeventhandler)的东西,这个文件事件处理器是单线程的,所以才有了redis是单线程的这一说法。
文件事件处理器的结构如下图:
它包含4个部分:
多个socketIO多路复用程序文件事件分派器事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)文件事件处理器采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器进行处理。多个socket可能会并发产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,会将socket产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。
来看redis客户端与服务端的一次通信过程:
1.接收连接请求:
客户端socket01向redis的serversocket请求建立连接,此时serversocket会产生一个AE_READABLE事件,
IO多路复用程序监听到serversocket产生的事件后,将该事件压入队列中。
文件事件分派器从队列中获取该事件,交给连接应答处理器。
连接应答处理器会创建一个能与客户端通信的socket01,并将该socket01的AE_READABLE事件与命令请求处理器关联。
2.读取请求内容:
假设此时客户端发送了一个setkeyvalue请求,此时redis中的socket01会产生AE_READABLE事件
IO多路复用程序将事件压入队列
事件分派器从队列中获取到该事件,由于前面socket01的AE_READABLE事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。
命令请求处理器读取socket01的keyvalue并在自己内存中完成keyvalue的设置。操作完成后,它会将socket01的AE_WRITABLE事件与命令回复处理器关联。
3.回复请求:
如果此时客户端准备好接收返回结果了,那么redis中的socket01会产生一个AE_WRITABLE事件,同样压入队列中,
事件分派器找到相关联的命令回复处理器
由命令回复处理器对socket01输入本次操作的一个结果,比如ok,之后解除socket01的AE_WRITABLE事件与命令回复处理器的关联。
这样便完成了一次通信。
redis事件处理伪代码
redis源码
篇幅原因这里就不贴redis的源代码,可以用vscode等工具打开redis安装目录下的src目录,通过全局搜索aeMain找到入口。
总结
起初当看到别人说redis是单线程时,很容易想成redis服务端只开启了一个线程用于做所有事情,然而实际上我们所说的redis单线程只是针对redis网络请求模块,即文中提到的文件事件处理器。
另外,在第一次看到文中第一张图时,就感觉像是一个线程(IO多路复用程序)负责往队列写数据,另一个线程(文件事件分派器)负责从队列里面读数据,那么redis的单线程到底体现在哪里呢?
最后通过一顿操作,翻到了相关源码,粗略一读才明白所谓的事件分派器、队列等等是这样的。