Redis设计与实现事件

编程

上一篇:Redis设计与实现-AOF持久化

前面将了Redis的底层数据结构与数据库相关知识,知道了5种数据类型在不同情况下采用何种数据结构,知道了数据库的基本框架以及它的一些特性,比如过期策略;知道了RDB和AOF持久化技术。接下来开始了解Redis是如何从客户端接收请求-执行请求-返回处理结果

概要

Redis是一个由事件驱动的程序,比如建立请求连接、执行命令等,所有的事件分为两种类型:文件事件和时间事件

接下来通常会先想到文件事件到底那些喃?但其实应该先想想Redis既然定义了事件,那他如何识别、接收、传递这些事件,因此得先看看Redis基于Reactor模式设计的网络事件处理器

但是在介绍Redis的网路事件处理器之前得先准备点知识。

准备知识点

 

什么是Reactor

摘抄自:https://www.jianshu.com/p/eef7ebe28673https://www.jianshu.com/p/eef7ebe28673

reactor设计模式是event-driven architecture的一种实现方式,处理多个客户端并发的向服务端请求服务的场景。每种服务在服务端可能由多个方法组成。reactor会解耦并发请求的服务并分发给对应的事件处理器来处理。目前,许多流行的开源框架都用到了reactor模式,如:netty、node.js等,包括java的nio

reactor reactor主要由以下几个角色构成:handle、Synchronous Event Demultiplexer、Initiation Dispatcher、Event Handler、Concrete Event Handler

Handle handle在linux中一般称为文件描述符,而在window称为句柄,两者的含义一样。handle是事件的发源地。比如一个网络socket、磁盘文件等。而发生在handle上的事件可以有connection、ready for read、ready for write等。

Synchronous Event Demultiplexer 同步事件分离器,本质上是系统调用。比如linux中的select、poll、epoll等。比如,select方法会一直阻塞直到handle上有事件发生时才会返回。

Event Handler 事件处理器,其会定义一些回调方法或者称为钩子函数,当handle上有事件发生时,回调方法便会执行,一种事件处理机制。

Concrete Event Handler 具体的事件处理器,实现了Event Handler。在回调方法中会实现具体的业务逻辑。

Initiation Dispatcher 初始分发器,也是reactor角色,提供了注册、删除与转发event handler的方法。当Synchronous Event Demultiplexer检测到handle上有事件发生时,便会通知initiation dispatcher调用特定的event handler的回调方法。

处理流程

当应用向Initiation Dispatcher注册Concrete Event Handler时,应用会标识出该事件处理器希望Initiation Dispatcher在某种类型的事件发生发生时向其通知,事件与handle关联

Initiation Dispatcher要求注册在其上面的Concrete Event Handler传递内部关联的handle,该handle会向操作系统标识

当所有的Concrete Event Handler都注册到 Initiation Dispatcher上后,应用会调用handle_events方法来启动Initiation Dispatcher的事件循环,这时Initiation Dispatcher会将每个Concrete Event Handler关联的handle合并,并使用Synchronous Event Demultiplexer来等待这些handle上事件的发生

当与某个事件源对应的handle变为ready时,Synchronous Event Demultiplexer便会通知 Initiation Dispatcher。比如tcp的socket变为ready for reading

Initiation Dispatcher会触发事件处理器的回调方法。当事件发生时, Initiation Dispatcher会将被一个“key”(表示一个激活的handle)定位和分发给特定的Event Handler的回调方法

Initiation Dispatcher调用特定的Concrete Event Handler的回调方法来响应其关联的handle上发生的事件

David John Wheeler

David John Wheeler 一个剑桥大学的计算机科学家,说了一句经典名句,不仅适合计算机,生活中也会非常好的指导意义

A famous aphorism of David Wheeler goes: "All problems in computer science can be solved by another level of indirection" (the "fundamental theorem of software engineering").[1] This is often deliberately mis-quoted with "abstraction layer" substituted for "level of indirection". An often cited corollary to this is, "...except for the problem of too many layers of indirection."

翻译:计算机科学中的任何问题都可以通过加上一层抽象层(间接层)来解决,除了抽象层的很多问题,比如:

计算机操作系统是对各种硬件设备的抽象,JAVA等语言是对机器语言的抽象,Spring是对Java的抽象

但是这些抽象给我们带来的问题就是出了问题不知道抽象底层的工作方式,很难查找问题

 

至于后面的"except for the problem of too many layers of indrection",比如:基于cacheline的伪共享 

 

什么是套接字

socket是一种编程接口,是一种特殊的文件描述符(fd),所以可以对它进行readwriteclose等操作,是对ip+port的抽象

 

文件事件

对上面的准备知识有一定了解之后再来看看Redis为文件事件设计的相关程序

文件事件处理器

文件事件处理器由套接字、IO多路复用程序、事件分派器、事件处理器四部分组成

文件事件处理器通过IO多路复用程序将多个客户端同时发起的请求放入一个队列当中,事件分派器串行取出一个套接字交给事件处理器完成后再取下一个进行处理

IO多路复用

redis支持5中IO多路复用技术:select poll epoll evport kqueue

其中select poll epoll是linux支持的多路复用技术,evport是Solaris的,kqueue是Mac Os和FreeBSD

Redis针对多种IO多路复用技术都进行了实现,由Redis自己决定到底使用高效的那一种,具体是实现方式是:

# ifdef HAVA_EVPORT

# include "ac_evport.c"

# else

# ifdef HAVE_EPOLL

# include "ae_epoll.c"

# else

# ifdef HAVE_KQUEUE

# include "ae_kqueue.c"

# else

# include "ae_select.c"

# endif

# endif

# endif

关于select poll epoll的区别:关注博客https://www.cnblogs.com/aspirant/p/9166944.html

补充:

select poll虽然不阻塞应用程序,但是select 和 poll函数本身是要阻塞的,一旦有套接字产生了数据,则内核会修改套接字的状态,这两个函数就会停止阻塞,接下来就循环遍历所有套接字找到有数据的套接字,然后根据READABLE状态对应的时间是连接还是传输数据,进行不同的处理。

事件类型

因为套接字在LINUX中被抽象为fd,而fd是事件的发源地,一个FD都有READABLEWRITABLECLOSE等事件,因此套接字也有相应的事件

READABLE

客户端执行writecloseconnect会产生套接字readable事件

WRITEABLE

客户端执行read会产生套接字writable事件

事件处理器

着重介绍连接应答、命令请求、命令回复三种文件事件处理器,为了更好的理解,补充一点内容:

内核中套接字有两个链表

1.正在建立连接(握手)的套接字,内核socket函数创建的

2.已经完成连接建立的套接字(新分配的fd)

既然是从内核可以区分建立连接和连接完成的套接字,那么就可以让程序将套接字绑定到不同的文件事件处理器上

连接应答

程序首先是将连接应答事件处理器与套接字的READABLE事件关联,当客户端执行connect时,服务端完成建立连接操作

命令请求

连接建立之后程序会将套接字的READABLE事件与命令请求事件处理器关联,读取套接字的内容,并执行命令

命令回复

当服务器有信息要写回客户端时,程序会将套接字的WRITABLE事件与命令回复事件处理器关联,当客户端执行read后,产生WRITABLE事件,引发命令回复事件处理器执行,最终将信息写入到套接字当中

时间事件

Redis时间事件分为定时和定期两种事件,其中典型的是定期事件的serverCron函数,它负责了非常多重要的数据库维护操作,比如:

  • 更新服务器的各类统计信息,比如时间、内存占用、数据库占用

  • 清理数据库中的过期键值对:删除策略之一:定期删除

  • 关闭和清理连接生效的客户端

  • 尝试进行AOF或者RDB持久化:redis.conf中的save配置

  • 如果服务器是主服务器,对从服务器进行定期同步

  • 如果处于集群模式,对集群进行定期同步和连接测试

为了更好的理解关于网络的内容可以参考:WireShark抓包分析

以上是 Redis设计与实现事件 的全部内容, 来源链接: utcz.com/z/512439.html

回到顶部