redis面试:深度解析Redis线程模型设计原理

Reactor设计模式

Reactor设计模式的基本设计思想是基于IO复用模型来实现的

  • 这里说下IO复用模型。与传统IO多线程阻塞不同,IO复用模型中多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象中等待。当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理
  • 什么意思呢?餐厅老板也发现了顾客点餐慢的问题,于是他采用了一种大胆的方式,只留了一个服务员。当客人点餐的时候,这个服务员就去招待别的客人,客人点好餐后直接喊服务员来进行服务。这里的顾客和服务员分别可以看作是多个连接和一个线程。服务器阻塞在一个顾客那里,当有别的顾客点好餐之后,他就立即去服务其他的顾客

了解了reactor的设计思想之后,再来看下但reactor单线程的实现方案

在这里插入图片描述

在这里插入图片描述
reactor通过IO复用程序监控客户端请求实践,收到事件后通过任务分派器进行分发。

  • 针对建立连接请求事件,通过acceptor处理,并建立对应的handle负责后继业务处理。
  • 针对非连接事件,reactor会调用对应的handler完成read->业务处理–>write处理流程,并将结果返回给客户端。

整个过程都在一个线程里面完成

redis的网络事件处理器

redis基于reactor模式开发了自己的网络事件处理器,这个处理器被称为文件事件处理器。

  • 采用I/O多路复用同时监听多个socket,根据socket当前执行的事件来为 socket 选择对应的事件处理器
  • 当被监听的socket准备好执行accept、read、wriet、close等操作时,和操作对于的文件事件就会产生,这个时候FEH就会调用socket之前关联好的事件处理器来处理对应的事件

整个过程都在一个线程里完成,因此 Redis 被称为是单线程的操作。

在这里插入图片描述
对于上面:

socket

  • 文件事件就是对socket操作的抽象
  • 每当一个 socket 准备好执行连接accept、read、write、close等操作时, 就会产生一个文件事件。
  • 一个服务器通常会连接多个socket, 多个socket可能并发产生不同操作,每个操作对应不同文件事件。

I/O多路复用程序

  • I/O 多路复用程序会负责监听多个socket。
  • 尽管文件事件可能并发出现, 但 I/O 多路复用程序会将所有产生事件的socket放入队列, 通过该队列以有序、同步且每次一个socket的方式向文件事件分派器传送socket。
  • 当上一个socket产生的事件被对应事件处理器执行完后, I/O 多路复用程序才会向文件事件分派器传送下个socket, 如下:
    -

I/O多路复用程序的实现

  • redis的IO多路复用程序的所有功能都是通过包装常见的select、epoll、evport和kqueue这些IO多路复用函数库实现的
  • 每个IO多路复用函数库在redis源码中都对应一个单独的文件
    在这里插入图片描述
  • 因为redis为每个IO多路复用函数库都实现了相同的API,所以IO多路复用程序的底层实现是可以互换的
  • 因为 Redis 为每个 I/O 多路复用函数库都实现了相同的 API , 所以 I/O 多路复用程序的底层实现是可以互换的。Redis 在 I/O 多路复用程序的实现源码ae.c文件中宏定义了相应规则,使得程序在编译时自动选择系统中性能最高的 I/O 多路复用函数库作为 Redis 的 I/O 多路复用程序的底层实现:性能降序排列。
    在这里插入图片描述

文件事件分派器

文件事件分派器接收IO多路复用程序传来的socket,并根据socket产生的事件类型,调用相应的事件处理器

文件事件处理器

服务器会为执行不同任务的套接字关联不同的事件处理器,这些处理器是一个个函数,它们定义了某个事件发生时,服务器应该执行的动作。

  • redis为各个文件事件需求编写了多个处理器,如果客户端
    • 连接redis,对连接服务器的各个客户端进行应答,就需要将socket映射到链接应答服务器
    • 写数据到redis,接收客户端传来的命令请求,就需要映射到命令请求处理器
    • 从redis读数据,向客户端返回命令的执行结果,就需要映射到命令回复处理器
  • 当主服务器和从服务器进行复制操作时, 主从服务器都需要映射到特别为复制功能编写的复制处理器。

文件事件的类型

I/O 多路复用程序可以监听多个socket的 ae.h/AE_READABLE 事件和ae.h/AE_WRITABLE事件, 这两类事件和套接字操作之间的对应关系如下:

  • 当socket可读(比如客户端对Redis执行write/close操作),或有新的可应答的socket出现时(即客户端对Redis执行connect操作),socket就会产生一个AE_READABLE事件
  • 当socket可写时(比如客户端对Redis执行read操作),socket会产生一个AE_WRITABLE事件。

I/O多路复用程序可以同时监听AE_REABLEAE_WRITABLE两种事件,要是一个socket同时产生这两种事件,那么文件事件分派器优先处理AE_REABLE事件。即一个socket又可读又可写时, Redis服务器先读后写socket。

最后,让我们梳理一下客户端和Redis服务器通信的整个过程:
在这里插入图片描述

  • redis启动初始化时,将连接应答处理器跟AE_READABLE事件关联。
  • 若一个客户端发起连接,会产生一个AE_READABLE事件,然后由连接应答处理器负责和客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。
  • 当客户端向Redis发请求时(不管读还是写请求),客户端socket都会产生一个AE_READABLE事件,触发命令请求处理器。处理器读取客户端的命令内容, 然后传给相关程序执行。
  • 当Redis服务器准备好给客户端的响应数据后,会将socket的AE_WRITABLE事件和命令回复处理器关联,当客户端准备好读取响应数据时,会在socket产生一个AE_WRITABLE事件,由对应命令回复处理器处理,即将准备好的响应数据写入socket,供客户端读取。
  • 命令回复处理器全部写完到 socket 后,就会删除该socket的AE_WRITABLE事件和命令回复处理器的映射。

Redis6.0的多线程

Redis6 版本中引入了多线程。之前已经提到过 Redis 单线程处理有着很快的速度,那为什么还要引入多线程呢?单线程的瓶颈在什么地方?

  • 先来看第二个问题,在 Redis 中,单线程的瓶颈主要在网络IO上。也就是在读写网络的read/write系统调用执行期间会占用大量的CPU时间。如果要对一些大的键值对进行删除操作的话,在短时间内是删不完的,那么对于单线程来说就会阻塞后边的操作。回想下上边讲的reactor模式中单线程的处理方式。针对非连接事件,Reactor 会调用对应的 handler 完成 read->业务处理->write 处理流程,也就是说这一步会造成性能上的瓶颈。

  • redis6.0的多线程是指,将网络数据读写和协议解析通过多线程的方式来处理,对于命令来说,仍然使用单线程操作。也就是说,redis6.0的多线程是为了解决其网络IO的瓶颈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值