likes
comments
collection
share

带你快速通关Redis网络模型

作者站长头像
站长
· 阅读数 38

几种IO模型

IO分为内存IO,磁盘IO,网络IO。本文中的IO以网络IO为例

先来看IO模型的对比。

阻塞IO

单线程阻塞

当client1端向server端发起连接请求之后,必须等待server端请求处理完成之后才可以向下继续运行。

再有client2向server端发送连接请求,只能等待client1关闭之后,才能处理client2的连接。

多线程阻塞

当client1端向server端发起连接请求之后,必须等待server端请求处理完成并且返回数据之后才可以向下继续运行。

再有client2端向server端发起连接,server端会创建另外一个线程去处理client2的发送的连接请求。

此时,如果client越来越多,开启的线程也越来越多,并且每一个线程只能处理自己绑定的那一个客户端,程序也需要保存每一个客户端的连接信息。在很少几个客户端的情况下,效率可能会很高,但是从高并发的角度看,资源消耗太大。

非阻塞IO

当client1端向server端发起连接请求后,会不断的向server端轮询是否成功。这样的话,CPU持续空转,占用大量资源。

带你快速通关Redis网络模型

IO多路复用

IO多路复用的思想是,server端不再需要去轮询等待内核空间的数据,而是采用事件触发机制。用一个线程去轮询各个客户端,只要数据准备好,立马交由处理线程去执行。这里主要讲述Redis网络模型,这里先略过。

关于IO多路复用的实现有以下常见的几种select(),poll(),epoll(),kqueue()等,Redis会选择操作系统提供的最优的IO接口作为实现。

Redis统一封装了系统调用函数,这样只需要调用统一的API即可。

带你快速通关Redis网络模型 带你快速通关Redis网络模型

了解了IO模型之后,我们来了解一下高并发网络编程模型**Reactor **

Reactor模型

模型中的角色

Reactor:负责分发事件,类似于IO多路复用的select
Acceptor:处理客户端连接线程
Handler:处理读、写事件

单Reactor单线程模式

带你快速通关Redis网络模型

处理流程

一个Reactor负责监听Socket,一旦有客户端连接、与其他读写事件,Reactor将连接操作交予Acceptor线程处理,而其他读写事件则交由Handler处理。

优点

  • 职责清晰,模块解耦简单,代码易编写
  • 在流量不是特别大、业务处理比较快的时候系统可以有很好的表现,

缺点

  • 连接处理和业务处理共用一个线程,无法充分利用CPU多核的优势。
  • 当流量比较大、读写事件比较耗时情况下,容易导致系统出现性能瓶颈。

Redis 6.0 之前,采用的便是这种结构。Redis直接对内存进行操作,所以执行速度非常快,性能瓶颈并不在CPU上。

单Reactor多线程模式

带你快速通关Redis网络模型

这种模型相对单Reactor单线程模型,只是将业务逻辑的处理逻辑交给了一个线程池来处理。

处理流程

  • Reactor监听连接事件、Socket事件,当有连接事件过来时交给Acceptor处理,当有Socket事件过来时交个对应的Handler处理。
  • Handler完成读事件后,包装成一个任务对象,交给线程池来处理,把业务处理逻辑交给其他线程来处理。

优点

  • 让主线程专注于通用事件的处理(连接、读、写),从职责上再进行细粒度的划分;

缺点

  • 貌似这种模型已经很完美了,我们再思考下,如果客户端很多、流量特别大的时候,通用事件的处理(读、写)也可能会成为主线程的瓶颈,因为每次读、写操作都涉及系统调用。

多Reactor多线程模式

带你快速通关Redis网络模型

这种模型相对单Reactor多线程模型,只是将Scoket的读写处理从mainReactor中拎出来,交给subReactor线程来处理。

处理流程

  • mainReactor主线程负责连接事件的监听和处理,当Acceptor处理完连接过程后,主线程将连接分配给subReactor。

  • subReactor负责mainReactor分配过来的Socket的监听和处理,当有Socket事件过来时交个对应的Handler处理。

  • Handler完成读事件后,包装成一个任务对象,交给线程池来处理,把业务处理逻辑交给其他线程来处理。

优点

  • 让主线程专注于连接事件的处理,子线程专注于读写事件,上进一步解耦;
  • 利用CPU多核的优势。

从Reactor模型中我们可以总结出,要实现一个Reactor模型,必须要有的几个核心组件,事件,事件监听,事件分发,事件处理,事件捕获。

带你快速通关Redis网络模型

Redis的网络模型

概述

Redis基于Reactor模型实现了自己的事件驱动框架。Redis 的事件驱动框架定义了两类事件

文件事件
客户端发送的命令
时间事件
Redis 自身的周期性操作。

例如下面是文件事件的数据结构

/* File event structure */
typedef struct aeFileEvent {
    int mask; 
    aeFileProc *rfileProc;  //读事件处理函数指针
    aeFileProc *wfileProc;  //写事件处理函数指针
    void *clientData; //客户端私有数据
} aeFileEvent;

模型图

带你快速通关Redis网络模型

IO多路复用程序负责各事件的监听(连接、读、写等),当有事件发生时,将对应事件放入事件有序队列中,由事件派发器根据事件类型来进行分发。

如果是连接事件,则分发至连接处理器,将连接的客户端与服务器进行绑定。如果是Redis命令则分发至命令请求处理器,命令处理完后产生命令回复事件,再由事件队列,到事件派发器,到命令回复处理器,回复客户端响应。

连接事件处理

带你快速通关Redis网络模型

  1. Redis监听固定端口,并将连接事件交由连接处理器处理。

  2. 客户端发起连接,触发连接事件,IO多路复用程序将连接事件包装好后放入事件队列,然后由事件派发器分发给连接应答处理器。

  3. 连接应答处理器创建client对象以及事件对象,并产生ae_readable事件,和命令处理器关联,标识后续该事件是可读事件,也就是开始接收客户端的命令操作。

注意:上述过程都是主线程执行。

请求事件处理

带你快速通关Redis网络模型

  1. 客户端发起命令,IO多路复用程序监听到该事件后(读事件),将数据包装成事件丢到事件队列中(事件在上个流程中绑定了命令请求处理器)。有读写事件则先处理读事件,再处理写事件。

  2. 事件分发处理器根据事件类型,将事件派发给对应的命令请求处理器。

  3. 命令请求处理器,读取事件中的数据,执行命令,然后产生ae_writable事件,并绑定命令回复处理器。

  4. IO多路复用程序监听到写事件后,将数据包装成事件丢到事件队列中,事件分发处理器根据事件类型分发至命令回复处理器。

  5. 命令回复处理器,将数据返回给客户端。

总结

Redis的网络编程模式,结合了IO多路复用以及高并发网络编程模式Reactor。这也是Redis为什么能这么快的核心原因之一。

转载自:https://juejin.cn/post/7244407068110995513
评论
请登录