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. 自制刻度尺插件-前端简易实现"腾讯信用"界面

    依据我现有的知识,在前端上"简易"的实现了腾讯信用的界面,同时自己自制了一个竖直的刻度尺插件,曲线的位置可以根据传入的数值动态的改变,这次主要也想总结一下关于jQuery中exte ...

  2. Jsp中格式化时间戳的常用标签

    接下来一段时间的记录,虽然不是一些什么全新的知识,但是作为后台开发人员都是应该必须掌握的前端基础技能,说实话,每一个后台程序员的短板就在于前端技能的弱而造就了不能更上一层楼.从今天开始,我将彻底开启菜 ...

  3. How to implement a custom PropertyEditor so that it supports Appearance rules provided by the ConditionalAppearance module

    https://www.devexpress.com/Support/Center/Question/Details/T505528/how-to-implement-a-custom-propert ...

  4. 【PaPaPa】集成B/S主流技术的MVC5项目 - 实干派:说做就做,我们已经起航,你还在观望吗

    我们是谁 我们是C#爱好者,互相分享技术,一起学习一起成长一起做一个项目. 我们是开源爱好者,从我们手上出来的代码都会托管在源代码管理平台(oschina),到目前为止不收费,将来也不会出现任何收费情 ...

  5. cnblogs客户端配置说明

    1. 下载地址 http://openlivewriter.org/ 2.安装 安装时设置好blog地址和账户.密码: 到这里基本上就算安装完成了.如果之前的自动配置没有成功,会出现一个界面让你配置b ...

  6. 木马分析出现python语言,360的安全人员不禁感叹还有这种操作?

    几年前,敲诈者木马还是一个默默无闻的木马种类.然而,由于其极强的破坏力和直接且丰厚的财富回报,敲诈者木马这几年已经一跃成为曝光率最高的木马类型——甚至超越了盗号木马.远控木马.网购木马这传统三强.与此 ...

  7. 如何配置php客户端(phpredis)并连接Redis--华为DCS for Redis使用经验系列

    使用php连接Redis.Memcache等都需要进行扩展,以CentOS为例,介绍phpredis的客户端环境搭建. 第0步:准备工作 华为云上购买1台弹性云服务器ECS(我选了CentOS 6.3 ...

  8. EOS博彩合约设计

    集中博彩游戏合约设计 一.功能接口 1. 质押deposit 由用户发起,用户将个人账户中token质押给平台,从而可以进入平台去参与平台活动. 2. 赎回withdraw 由用户发起,在用户结束平台 ...

  9. [整理]CHttpConnection的使用

    使用步骤: 1.构造一个CInternetSession的实例 CInternetSession* pSession =new CinternetSession(); //CInternetSessi ...

  10. paste命令详解

    基础命令学习目录首页 原文链接:https://blog.csdn.net/u011341352/article/details/52806312 个人分类: linux   paste命令和cut命 ...