首页 > 数据库 >Redis事件

Redis事件

时间:2024-04-03 10:58:33浏览次数:14  
标签:Redis 文件事件 事件 接字 客户端 服务端 处理器

前言

我们有的时候会去想Redis是如何去处理客户端的请求的.服务端又是如何和客户端进行通信的,又是如何去处理一些定时操作的,这就是我们这篇文章想要了解的东西.

而Redis将这些事件分为俩种:文件事件,时间事件

  • 文件事件: 服务端和客户端之间通过套接字进行通讯,而在这些通讯的过程会产生文件事件,Redis程序会监听并处理这些文件事件来完成网络通信的功能.
  • 时间事件: 有时候Redis需要定时去处理一些逻辑,比如在某个时间段或是每隔多少时间去做某个操作,我们称这些行为是时间事件.

文件事件

Reactor模型

Redis基于Reactor网络模型实现了一套属于自己的网络事件处理器.
那么这个Reactor网络模型又是什么呢?
Reactor以多路IO复用为思想,以事件为驱动.
在早期网络IO时,当客户端与服务端通过套接字连接成功之后,程序对于每一个套接字都要分出一个线程去监听其套接字并进行处理,即建立连接,数据读写,业务逻辑处理等阶段.
而每一个线程在进行建立连接,数据读写等操作的时候,需要进行系统调用,由用户态转换到系统态,我们可以认为这种操作是比较消耗资源和耗时的.而用户线程又得等待这些IO操作完成之后才可以进行具体业务逻辑处理的操作.
这样可能会导致大多数用户线程都在等待状态中.

但是网络模型在演进的过程中逐渐将这些阶段进行拆分,使得每一阶段都可以让我们单独选择使用单线程还是线程池来实现,可以极大的节省资源又提高了性能.

通过这种网络模型阶段的拆分,我们可以在应用中只使用一个专门的线程来处理所有的套接字通讯,该线程不断地轮询非阻塞查询套接字的IO事件,如果对应套接字已经准备好进行IO事件,才会进行系统调用来进行数据读写.
而用户线程不需要长时处于等待状态.应用只需要使用一个专门的线程来轮询监听套接字即可.
这就是多路复用.

非阻塞指的是:想要对某个套接字进行数据读写等IO操作时,先进行非阻塞查询IO事件是否就绪,如果没有就绪则直接返回,如果就绪了则才会真正的开始数据读写.
但是如果是阻塞IO,则会等待该套接字的IO事件就绪,然后开始数据读写.

事件驱动又是什么呢?

  • 即是当轮询线程检测到套接字的IO事件就绪之后,以事件的形式通知对应业务线程进行数据读写并处理.
    具体事件的形式是什么样的呢? 这个我们接下来在可以通过介绍Redis的文件事件处理器来进一步了解.

参考文章:https://juejin.cn/post/7092436770519777311

文件事件处理器

  • Redis的文件事件处理器采用多路复用程序来监听多个套接字,并根据套接字现在执行的任务来为套接字关联不同的事件处理器.
  • 当所监听的套接字发生了连接,读取,写入,断开等操作的时候,文件事件处理器会生成与操作所对应的事件,然后将事件交给套接字绑定的的事件处理器来处理这些事件.

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

套接字: 计算机可以通过套接字来给其他计算机发送数据或是接受其他计算机发送的数据.

IO多路复用程序: 监听多个套接字,来寻找发生了连接,读取,写入,断开等文件事件的套接字,而这些文件时间也是对应套接字操作的抽象,当对应套接字发生对应的操作时,就会产生对应的文件事件,然后通过套接字文件事件关联的事件处理器来处理这些事件.

IO多路复用程序用于监听套接字的ae.h/AE_READABLE事件与ae.h/AE_WRITEABLE事件
当客户端对套接字进行connect,write,close操作时,使该套接字对于服务端来说可读,则会产生ae.h/AE_READABLE事件
当客户端对套接字进行read操作时,使该套接字对于服务端可写,则会产生ae.h/AE_WRITEABLE事件
虽然我们这里只有俩个事件,但是在不同的情况下,服务端会给其关联不同的事件处理器进行处理.比如一开始会为服务端的监听套接字的ae.h/AE_READABLE关联连接应答处理器.
而客户端与服务端连接成功后,则会为客户端套接字的ae.h/AE_READABLE关联命令请求处理器
而在客户端对服务端请求命令时,服务端要为命令进行回复,则要将客户端套接字的ae.h/AE_WRITEABLE事件关联命令回复处理器.

文件事件分派器: 当有事件发生的时候,IO多路复用程序会通知文件事件分派器,然后文件事件分派器去通知对应的事件处理器去处理.

可以有多个客户端与Redis服务端进行连接,那就是说明可能同一时间可能有多个套接字同时产生事件,这样会出现并发操作,但是IO多路复用器会将这些产生了事件的多个套接字放入一个队列之中,以有序,同步,一次一个套接字的方式向文件事件分派器来发送套接字,只有当上一个套接字的事件处理完毕后,才会将下一个套接字发送出去.

事件处理器: 服务器编写了多个用于处理网络通信时所需的处理器,比如连接应答处理器,命令请求处理器,命令回复处理器,复制处理器等等,根据不同的情况,会将不同的事件处理器关联到该套接字的事件上.

一次完整的文件事件示例

服务端的套接字的ae.h/AE_READABLE事件关联着连接应答处理器,当客户端对服务端套接字发起connect函数之后,服务端会产生ae.h/AE_READABLE事件,然后交由连接应答处理器处理.
连接应答处理器会创建客户端的套接字,并且初始化客户端的状态,将客户端套接字的ae.h/AE_READABLE关联命令请求处理器.
当客户端发起命令请求时,会将请求交由命令请求处理器来进行处理,当处理完毕后,服务端想要对结果进行回复,则会将客户端套接字的ae.h/AE_WHRITEABLE事件关联命令回复处理器.
这样当客户端对套接字发起read函数时,服务端就使用命令回复处理器将处理结果写入到套接字中.当写入完毕后,再解除ae.h/AE_WHRITEABLE事件与命令回复处理器之间的关联.


时间事件

Redis时间事件的分类

我们上面说过,我们称某个时间点和每隔一段时间需要执行的操作,都称为文件事件,所以文件事件也分为俩类:

  • 定时事件: 指在某个固定时间点进行执行的操作,只需要执行一次即可.
  • 周期事件: 指需要每个一段时间就需要执行一次的操作,需要往复执行.

时间事件的属性

  • id: 全局标记一个时间事件的唯一id,从小到大递增,旧事件的id值比新事件的id值小
  • when: 记录了该时间事件的到达时间
  • timeProc: 时间事件处理器,当时间到达后服务端就会调用其时间事件处理器来处理事件

那么服务端是如何区分定时时间和周期事件呢?

  • 会根据时间事件处理器的返回值来处理,如果该时间处理器函数的返回值为AE_NORMAL,则说明这个时间是一个定时事件,如果是其他的整数值,则会根据整数值来重新设置该时间事件的when属性.
    比如一个周期事件的when值是1712042488,timeProc返回了30毫秒,就会将when更新为1712042488+30=1712042518,让这个时间事件在30毫秒后再次被执行.

实现

服务端将这些时间事件存储在一个链表之中,这个链表根据id从大到小排序,也就是新的时间事件会被放到链表头部.
每当时间事件执行器执行时,它就会遍历这个链表之中全部的时间事件,然后检查现在时间是否比链表中时间事件的when属性大,如果有则调用其时间处理器.

serverCorn函数

serverCorn函数就是服务端默认执行的一个周期性时间事件,虽然我们上面说Redis是支持将多个时间事件存放在链表中,同时存在多个时间事件的.
但是其实该版本的Redis中正常情况下,时间事件只有一个serverCorn函数.该函数有许多职责:

  • 清理过期键值对
  • 清理失效或是超时的客户端
  • 更新各类统计信息,比如内存占用,命令执行次数等等
  • 如果是主服务器对于从服务器进行同步
  • 集群模式下集群的定期同步和连接测试等等.

事件的调度与执行

我们经常听说Redis在执行任务的时候是单线程进行执行的,那么时间事件和文件事件的调度与执行是怎么样的呢?什么时候调用时间事件?什么时候调用文件事件?

不管是时间事件还是文件事件,事件的调度和执行都是由ae.c/aeProcessEvents函数来处理的,我们可以先来看下这个函数的伪代码演示

def aeProcessEvents():
  #获取到达时间最少的的时间事件
  time_event = aeSearchNearestTimer();
  #计算当前时间距离该时间事件的到达时间还有多久
  remaind_ms = time_event.when - unix_ts_now();
  #因为可能该时间事件的到达时间已经过去,则设置为0
  if remaind_ms < 0:
    remaind_ms = 0
  #使用该时间差创建一个timeval结构体
  timeval = create_timeval_with_ms(remaind_ms)
  #寻找是否有文件事件产生,如果没有则阻塞等待文件事件产生,直到超过传入timeval中的时间,则返回
  #如果timeval为0则调用之后直接返回
  aeApiPoll(timeval)
  #处理所有的文件事件
  processFileEvents()
  #处理所有的时间事件
  processTimeEvents()

当进行时间的处理时,先计算到达时间最小的时间事件,用该时间作为阻塞等待文件事件的参数,即使长时间没有文件事件产生也不会影响时间事件的执行.
而对于文件事件和时间事件的执行都是同步,有序的执行,不需要担心抢占或是并发问题等.
文件事件中如果命令回复时,数据量太大可以保留等待下一次的时候再继续写入
时间事件中耗时的持久化操作都是由子线程去做的
所以不用担心每次操作会执行非常久影响效率

处理所有的文件事件processFileEvents()函数只是伪代码,实际上代码里对于文件事件的处理是直接在aeProcessEvents()函数中的.

实际上就是在循环中不停调用该事件处理aeProcessEvents()函数,再加上一些初始化和销毁的函数来构成了Redis的主函数
伪代码:

def main():
  init_server()
  while(server_is_not_shutdown()):
    aeProcessEvents()
  clean_server()

标签:Redis,文件事件,事件,接字,客户端,服务端,处理器
From: https://www.cnblogs.com/youjunhui/p/18092819

相关文章

  • Redis高可用
     持久化:持久化是最简单的高可用方法,主要作用:数据备份,即将数据存储在硬盘保证数据不会因进程退出而丢失 主从模式:主从复制时高可用Redis的基础。主动复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。 哨兵:在主从复制的基础上实现了自动化的故障......
  • Redis
    redis-cliSADDcities1北京上海广州深圳杭州苏州南京成都 redis-cliSADDcities2昆明哈尔滨济南厦门合肥佛山南昌兰州 redis-cliSADDcities3银川丽江保定三亚桂林襄阳redis-cliSMOVEcities2cities1昆明redis-cliSUNIONSTOREcitiescities1ci......
  • MySQL、Redis 和 Zookeeper 实现分布式锁方法及优缺点
    MySQL、Redis和Zookeeper都可以用来实现分布式锁,每种技术都有其特定的实现方法以及各自的优缺点。MySQL分布式锁实现方法在MySQL中实现分布式锁通常涉及到使用数据库表。可以创建一个专用的锁表,并利用行的唯一性(例如利用唯一索引)来实现锁机制。使用基于事务的 FORUP......
  • redis-BitMap(位图)使用方法
    一,BitMap介绍使用位存储,信息状态只有0和1Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。二,应用场景签到统计、状态统计三,命令命令 描述setbitkeyoffsetvalue 为指定key的offset位设置值getb......
  • 非关系型数据库——Redis基本操作
    目录一、Redis数据库常用命令1.Set——存放数据 2.Get——获取数据3.Keys——获取符合条件的键值4.Exists——判断键值是否存在5.Del——删除指定键值6.Type——获取键值对应的类型7.Rename——对已有键值重命名(覆盖)8.Renamenx——对已有键值重命名(不覆盖)9.Dbsize—......
  • redis特殊数据类型-Geospatial(地理位置)用法
    一 Geospatial(地理位置)介绍使用经纬度定位地理坐标并用一个有序集合zset保存,所以zset命令也可以使用有效的经度从-180度到180度。有效的纬度从-85.05112878度到85.05112878度。二 Geospatial应用场景        通过georadius就可以完成附近的人功能withcoo......
  • Redis 高可用之持久化
    一.高可用相关知识1.什么是高可用在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。但是在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量......
  • redis数据类型
    以下是Redis的主要数据类型及其使用场景:字符串(string)使用场景:存储用户信息、缓存热点数据等。特性:字符串是Redis最基本的数据类型,支持修改操作,可以用于实现计数器、分布式锁等功能。哈希(Hash)使用场景:存储用户信息、配置信息等。特性:哈希是键值对的集合,提供了存储字......
  • HTML设置定时执行代码 JavaScript 计时事件
    1、https://www.runoob.com/js/js-timing.htmlJavaScript一个设定的时间间隔之后来执行代码我们称之为计时事件JavaScript计时事件通过使用JavaScript,我们有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。我们称之为计时事件。在JavaScript......
  • 【Redis教程0x0C】数据库与缓存的一致性保证
    1.引言当我们在实现业务的过程中,如果发现服务器的性能瓶颈在数据库时,就要考虑加上Redis,让它作为数据库的缓存了。这样,客户端请求数据时,如果能在缓存命中,就不用去查数据库了,这大大减轻了数据库的压力,提高了服务器性能。那么这里就产生了个问题,我们在数据更新的时候,既需要......