Redis 事件处理

Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:

  • 文件事件(file event):Redis服务器通过套接字与客户端(或其他Redis服务器)进行连接,而文件事件就是服务器对套接字操作的抽象。服务器与客户端(或其他服务器)的通信会产生相应的文件事件,而服务器则通过监听并处理这些事件来完成一系列网络通信操作;
  • 时间事件(time event):Redis服务器的一些操作(比如serverCron函数)需要在给定的时间执行,而时间事件就是服务器对这类定时操作的抽象;

文件事件

1.文件事件处理器

  • 文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器;
  • 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的时间处理器来处理这些事件;

2.组成

文件事件处理器由四个组成部分:套接字、I/O多路复用程序、文件事件调度器,以及事件处理器;

文件事件是对套接字操作的抽象,每当一个套接字准备好执行连接应答、写入、读取、关闭等操作时,就会产生一个文件事件。因为一个服务器通常会连接多个套接字,所以多个文件事件有可能会并发地出现。

I/O多路复用程序负责监听多个套接字,并向文件时间调度器传送那些产生了事件的套接字。尽管多个文件事件可能会并发地出现,但I/O多路复用程序总是会将所有产生事件的套接字都放到一个队列里面,然后通过这个队列以有序、同步、每次一个套接字的方式向文件事件调度器传送套接字。如下如所示:

文件事件调度器接收I/O多路复用程序传来的套接字,并根据套接字产生的事件类型,调用相应的时间处理器;时间处理器是一个个函数,它们定义了某个事件发生时,服务器该执行的动作。

3.I/O多路复用程序

Redis的I/O多路复用程序的所有功能都是通过包装常见的select、epoll、evport和kqueue这些I/O多路复用函数来实现的,每个I/O多路复用函数库在Redis源码中都对应一个单独的文件,比如ae_select.c、ae_epoll.c、ae_kqueue.c诸如此类。因为Redis为每个I/O多路复用函数库都实现了相同的API,所以I/O多路复用程序的底层实现是可以互换,如下如所示:

Redis在I/O多路复用程序的实现源码中用#include宏定义了相应的规则,程序会在编译时自动选择系统中性能最高的I/O多路复用函数库来作为Redis的I/O多路复用程序的底层实现:

# ifdef HAVE_EVPORT
# include "ae_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

事件类型

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

  • 当套接字变得可读时(客户端对套接字执行write操作或执行close操作),或者有新的可应答(acceptable)套接字出现时(客户端对服务器的监听套接字执行connect操作),套接字产生AE_READABLE事件;
  • 当套接字变得可写时(客户端对套接字执行read操作),套接字产生AE_WRITABLE事件;

I/O多路服用程序允许服务器同时监听套接字的AE_READABLE事件和AE_WRITABLE事件,如果一个套接字同时产生了这两种事件,那么文件事件调度器会优先处理AE_READABLE事件,等到AE_READABLE事件处理完成之后,才处理AE_WRITABLE事件。

1.API

  • ae.c/aeCreateFileEvent函数接受一个套接字描述符、一个事件类型,以及一个事件处理函数作为参数,将给定套接字的给定事件键入到I/O多路复用程序的舰艇范围,并对事件和时间处理器进行关联;
  • ae.c/aeDeleteFileEvent函数接受一个套接字描述符和一个监听事件类型作为参数,让I/O多路复用程序程序取消对给定套接字的给定事件的监听,并取消事件和事件处理之间的关联;
  • ae.c/aeGetFileEvents函数接受一个套接字描述符,返回该套接字正在被监听的事件类型:

    如果套接字没有任何事件被监听,那么函数返回AE_NONE;

    如果套接字的读事件正在被监听,那么函数返回AE_READABLE;

    如果套接字的写事件正在被监听,那么函数返回AE_WRITABLE;

    如果套接字的读事件和写事件正在被监听,那么函数返回AE_READABLE|AE_WRITABLE;
  • ae.c/aeWait函数接受一个套接字描述符、一个事件类型和一个毫秒数为参数,在给定的时间内阻塞并等待套接字的给定类型事件产生,当事件成功产生,或者等待超时后,函数返回;
  • ae.c/aeApiPoll函数接受一个sys/time.h/struct timeval结构为参数,并在指定时间内,阻塞并等待所有被aeCreateFileEvent函数设置为监听状态的套接字产生文件事件,当有至少一个事件产生,或者等待超时后,函数返回;
  • ae.c/aeProcessEvent函数是文件事件调度器,它先调用aeApiPoll函数来等待事件产生,然后遍历所有已产生的事件,并调度相应的时间处理器来处理这些事件;
  • ae.c/aeGetApiName函数返回I/O多路复用程序底层所使用的I/O多路复用函数库的名称:返回"select"表示底层为select函数库,诸如此类;

2.文件事件的处理器

连接应答处理器:networking.c/acceptTcpHandler函数是Redis的连接应答处理器,这个处理器用于对连接服务器监听套接字的客户端进行应答,其主要调用anet.c中的anetTcpAccept函数实现,具体实现为sys/socket.h/accept函数的包装。当Redis服务器进行初始化的时候,程序会将这个连接应答处理器和服务器监听套接字的AE_READABLE事件关联起来,当有客户端用sys/socket.h/connect函数连接服务器监听套接字的时候,套接字就会产生AE_READABLE事件,引发连接应答器执行,并执行相应的套接字应答操作;

命令请求处理器:networking.c/readQueryFromClient函数是Redis的命令请求处理器,这个处理器负责从套接字中读入客户端发送的命令请求内容,具体实现为unistd.h/read函数的包装。当一个客户端通过连接应答处理器成功连接到服务器之后,服务器会将客户端套接字的AE_READABLE事件和命令请求处理器关联起来,当客户端向服务器发送命令请求的时候,套接字就会产生AE_READABLE事件,引发命令请求处理器执行,并执行相应的套接字读入操作。在客户端连接服务器的整个过程中,服务器都会一直为客户端套接字的AE_READABLE事件关联命令请求处理器。

命令回复处理器:networking.c/sendReplyToClient函数是Redis的命令回复处理器,这个处理器负责将服务器执行命令后得到的命令回复通过套接字返回给客户端,具体实现为unistd.h/wirte函数的包装。当服务器有命令回复需要传送给客户端的时候,服务器会将客户端套接字的AE_WRITABLE事件和命令回复处理器关联起来,当客户端准备好接收服务器传回的命令回复时,就会产生AE_WRITABLE事件,引发命令回复处理器执行,并执行相应的套接字写入操作;

时间事件

时间事件分为以下两类:

  • 定时事件:让一段程序在指定的时间之后执行一次,比如,让程序A在当前时间30秒后执行一次;
  • 周期性事件:让一段程序每隔指定时间就执行一次,比如,让程序B每隔30秒就执行一次;

时间事件主要有以下三个属性构成:

  • id:服务器为时间事件创建的全局唯一ID(标识),ID按从小到大的顺序递增,新事件的ID号比旧事件的ID号要大;
  • when:毫秒精度的UNIX时间戳,记录了时间事件的到达(arrive)时间;
  • timeProc:时间事件处理器,一个函数。当时间事件到达时,服务器就会调用相应的处理器来处理事件;

一个时间事件是定时事件还是周期性事件取决于时间处理器的返回值:

  • 如果事件处理器返回ae.h/AE_NOMORE,那么这个事件为定时事件,该事件在达到一次之后就会被删除,之后不会再到达;
  • 如果事件处理器返回一个非AE_NOMORE整数值,那么这个事件为周期性事件:当一个时间事件达到之后,服务器会根据事件处理器返回的值,对时间事件的when属性进行更新,让这个事件在一段时间之后再次到达,并以这种方式一直更新并运行下去。比如,如果一个时间事件的处理器返回整数值10,那么服务器应该对这个时间事件进行更新,让这个事件在10毫秒之后再次到达(现在Redis主要使用这个);

1.API

  • ae.c/aeCreateTimeEvent函数接受一个毫秒数milliseconds和一个时间事件处理器proc作为参数,将一个新的时间事件添加到服务器,这个新的时间事件将在当前时间的milliseconds毫秒之后达到,而事件的处理器为proc;
  • ae.c/aeDeleteFileEvent函数接受一个时间事件ID作为参数,然后从服务器中删除该ID所对应的时间事件;
  • ae.c/aeSearchNearestTimer函数返回到达事件距离当前时间最接近的那个时间事件;
  • ae.c/processTimeEvents函数是时间事件的执行器,这个函数会遍历所有已到达的时间事件,并调用这些事件的处理器。已到达指的是,时间事件的when属性记录的UNIX时间戳等于或小于当前时间的UNIX时间戳。

processTimeEvents函数的定义可以用下面的伪代码来描述:

def processTimeEvents():
# 遍历服务器中的所有时间事件
for time_event in all_time_event():
# 检查事件是否已经到达
if time_event.when <= unix_ts_now():
# 事件已到达
# 执行事件处理器,并获取返回值
retval = time_event.timeProc()
# 如果这是一个定时事件
if retval == AE_NOMORE:
# 那么将该事件从服务器中删除
delete_time_event_from_server(time_event)
# 如果这是一个周期性事件
else:
# 那么按照事件处理器的返回值更新时间事件的 when 属性
# 让这个事件在指定的时间之后再次到达
update_when(time_event, retval)

2.时间事件应用实例:serverCron函数

持续运行的Redis服务器需要定期对自身的资源和状态进行检查和调整,从而确保服务器可长期、稳定地运行,这些定期操作由redis.c/serverCron函数负责执行,它的主要工作包括:

  • 更新服务器的各类统计信息,比如时间、内存扎用、数据库使用情况等;
  • 清理数据中的过期键值对;
  • 关闭和清理连接失效的客户端;
  • 尝试进行AOF或RDB持久化操作;
  • 如果服务器是主服务器,那么对从服务器进行定期同步;
  • 如果处于集群模式,对集群进行定期同步和连接测试;

3.事件的调度与执行

事件的调度和执行由ae.c/aeProcessEvents函数负责,可以用以下源代码完成:

def aeProcessEvents():
# 获取到达时间离当前时间最接近的时间事件
time_event = aeSearchNearestTimer()
# 计算最接近的时间事件距离到达还有多少毫秒
remaind_ms = time_event.when - unix_ts_now()
# 如果事件已到达,那么remaind_ms的值可能为负数,将它设定为0
if remaind_ms < 0:
remaind_ms = 0
# 根据remaind_ms的值,创建timeval结构
timeval = create_timeval_with_ms(remaind_ms)
# 阻塞并等待文件事件产生,最大阻塞时间由传入的timeval结构决定
# 如果remaind_ms的值为0,那么aeApiPoll调用之后马上返回,不阻塞
aeApiPoll(timeval)
# 处理所有已产生的文件事件(其实并没有这个函数)
processFileEvents()
# 处理所有已到达的时间事件
processTimeEvents()

Redis 基础:Redis 事件处理的更多相关文章

  1. redis基础:redis下载安装与配置,redis数据类型使用,redis常用指令,jedis使用,RDB和AOF持久化

    知识点梳理 课堂讲义 课程计划 1. REDIS 入 门 (了解) (操作)   2. 数据类型 (重点) (操作) (理解) 3. 常用指令   (操作)   4. Jedis (重点) (操作) ...

  2. 【Redis】Redis基础 - Redis安装启动测试

    Redis基本 - 安装 文章目录 Redis基本 - 安装 Linux下安装Redis Docker 方式 Github 源码编译方式 直接安装方式 Windows下Redis安装 记录 - Red ...

  3. redis基础----->redis的基本使用(一)

    这里我们就在虚拟机中安装redis,并且使用java和python实现简单的操作.深情是我承担不起的重担,情话只是偶尔兑现的谎言. redis的使用 下载地址:https://redis.io/.安装 ...

  4. redis 基础 Redis 数据类型

    String(字符串) Hash(哈希) List(列表) Set(集合) zset(sorted set:有序集合)

  5. mysql主从复制、redis基础、持久化和主从复制

    一.mysql(mariadb)基础 1.基础命令(centos7操作系统下) 1.启动mysql systemctl start mariadb 2.linux客户端连接自己 mysql -uroo ...

  6. linux - redis基础

    目录 linux - redis基础 redis 源码编译安装 redis 数据结构 1. strings类型 2. list 类型 3. sets集合类型 有序集合 5. 哈希数据结构 centos ...

  7. Redis基础知识点面试手册

    Redis基础知识点面试手册 基础 概述 数据类型 STRING LIST SET HASH ZSET(SORTEDSET) 数据结构 字典 跳跃表 使用场景 会话缓存 缓存 计数器 查找表 消息队列 ...

  8. redis 基础

    一 redis数据类型redis支持5种类型的数据类型,它描述如下的:1. 字符串 Redis字符串是字节序列.Redis字符串是二进制安全的,这意味着他们有一个已知的长度没有任何特殊字符终止,所以你 ...

  9. Redis基础知识之————如何处理客户端连接

    redis 连接建立 Redis Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作: 首先,客户端 ...

  10. windows下使用redis,Redis入门使用,Redis基础命令

    windows下使用redis,Redis入门使用,Redis基础命令 >>>>>>>>>>>>>>>> ...

随机推荐

  1. python中函数的定义和详细的使用方法

    1. 函数的概念,函数是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集   2. 函数的作用,使用函数可以加强代码的复用性,提高程序编写的效率   3. 函数的使用,函数必须先创建才 ...

  2. hdu1285确定比赛名次(拓扑排序+优先队列)

    传送门 第一道拓扑排序题 每次删除入度为0的点,并输出 这题要求队名小的排前面,所以要用到重载的优先队列 #include<bits/stdc++.h> using namespace s ...

  3. java两年工作经验有什么经验

    这两年里,了解了完整项目的开发过程,知道如何快速入手一个完全没接触过的项目:譬如先了解数据库关系后,马上熟悉一个功能从前端到后端的实现过程,自己再写一个功 能,这样子就能马上上手开发项目,之后在慢慢了 ...

  4. flask的继承和包含

    为了方便使用重复的页面,我们也可以使用继承模板.还有包含模板,一般使用包含,俩个都不是很好理解,我只是用完的理解简单介绍一下,他们的用法打不相同,却又有类似之处 我们访问页面的时候在最上边会有导航的信 ...

  5. 在初学Flask中遇到的小问题。

    今天初步认识到了Flask这个轻量级web开发框架.对于初步认识框架的我,暂时的有了一个小小的认识, 因为初学.也没有什么代码可供参开.一下的就是一个小小的例子. # 导包from flask imp ...

  6. 解决网速慢时maven仓库访问慢

    构建maven项目时会下载很多依赖,会从官网地址下载是个外国网站,访问速度会很慢,但可以通过修改maven的settings.xml文件换成国内的镜像地址就可以加快访问速度: 一.找到settings ...

  7. 为 GlusterFS 设计新的xlator (编译及调用过程分析)

    GlusterFS 是一个开源的网络分布式文件系统,前一阵子看了一点GlusterFS(Gluster)的代码,修改了部分代码,具体是增加了一个定制的xlator,简单记录一下. Gluster与xl ...

  8. 关于go语言中的WaitGroup

    如果你刚接触Go语言并且想用它构建高并发,高性能的应用,弄明白WaitGroups是怎么回事很重要. 在本教程中,我们将掌握以下内容: WaitGroups的用途 一个WaitGroups的简单示例 ...

  9. tr命令详解

    基础命令学习目录 原文链接:https://www.cnblogs.com/ginvip/p/6354440.html 什么是tr命令?tr,translate的简写,translate的翻译: [t ...

  10. Beta阶段基于NABCD评论作品

    组名:杨老师粉丝群 组长:乔静玉 组员:吴奕瑶  刘佳瑞  公冶令鑫  杨磊  刘欣  张宇  卢帝同 一.拉格朗日2018--<飞词> 1.1.NABCD分析 N(Need,需求):该小 ...