关于Redis事件机制的一些记录和思考
2022-02-18 13:47:01 13 举报
redis事件处理机制 和 nacos配置更新机制的分析和思考
作者其他创作
大纲/内容
5. v=3
众所周知,Redis本质是一个事件驱动型的缓存系统,在Redis系统模型中,事件被分为两种,一种是文件事件(FileEvent),一种是时间事件(TimeEvent)。可以按照下面简单的理解:
继续,设置好频率后,那么一个致命问题来了 -> redis是如何执行周期事件的呢?如果你又开发javaweb应用的经验,你肯定知道最简单的方式就是使用spring内置的定时器:@schedue(cron=xxx)
2. push v=3
都有问题,难道就没有更好的方案了?有!nacos配置更新里面是这么玩的:
client
server
2. set v=3
3. set v=3
2. wait
1. 有没有更新
1. 文件事件就是我们各种 java、go、python 客户端连接redis服务器后,产生的文件读写的事件,所谓的文件读写其实就是数据读写,比如`SET KEY VALUE`,亦或是`GET KEY`,这些命令都被redis封装成一个文件的读写事件,然后处理事件;(主要还是io多路复用,不细说了,看图 ->)
4. v=3
zhang san
3. v=3
desc
其实很简单,在client端死循环不停地发请求问,但是在server端接收到请求后,如果有更新就立刻返回给client告诉ta,哥们赶紧更新了!如果没有更新就等待10秒钟左右的时间,这期间如果有更新就返回更新数据,如果没有就返回空,让client继续请求自己,开始下一轮询问!
?
我们最容易想到的第一个方案就是推数据过去,只要server端更新后立刻请求client端:
这个模型有什么缺点呢,也很明显,很多时候server并没有数据更新,但是client不知道呀,所以只能频繁的问server到底自己需不需要更新,这里就有很多无效的请求。你也许会说,那就把请求的频率降低不就行了吗,比如没6秒请求一次,是可以!但是这样数据更新就不及时了啊!
方案一:
这个模型有什么缺点呢,很明显,server需要记录所有的client,如果有新增的client也需要有注册动作,如果有多种数据类型的话,甚至还要记录每个client需要什么类型的数据。这个对服务端来说是一个很繁重的事情。。。
Part-4扩展
首先,我们能想到既然是周期性事件,肯定有周期频率,那么这个频率是多少呢?Redis其实把这个给到了使用者,我们可以自己配置 ->
hz 10,赫兹是个中学物理概念,代表震动频率。10的意思是每秒震动10次,简单换算(period = 1/10s = 100ms),就是每100ms调度执行一次。
Part-2介绍
怎么样?体会到了吧,通过将wait操作移至server端既可以减少无效请求,又可以即时更新数据,和redis的fileEvent超时事件处理思想很相似,很巧妙的解决了一个棘手的问题,这个是很值得我们在设计中借鉴参考的!
java中的schedule定时执行,其实是一个多线程执行,jvm会另起一个线程单独周期性的执行定时任务,那么redis也是这么做的吗?如果不是,可以这么做吗?(纯属个人理解,有不对的就当看个笑话)首先,很遗憾,redis不是这么玩的。redis并没有多起一个线程或者进程来执行时间事件,而是将时间事件和文件事件放到一起,顺序执行,然后无限循环下去,伪代码:----------------------------while(true) { processFileEvent(); //处理client命令 processTimeEvent(); //处理时间事件}----------------------------其次,redis 可以另起线程处理时间事件吗?技术上当然没问题,但是为啥不呢?我觉得时间事件里面有很多是要动缓存数据的,比如清理过期键,多线程同时操作缓存,那就出现了一个更恶心的问题,资源竞争。要处理竞争关系能怎么办呢,悲观锁?乐观锁?不管什锁都会带来性能损耗,背离了redis高性能的初衷,redis的作者觉得,内存操作本来就快,单线程完全ok。所以放到一起顺序处理,但是放到一起又引入了其他问题,我们来一一的说下:
由此可以看出,redis作者精心设计出单线程处理事件精巧模型!其实当了解到这个设计后,我立刻想到了另外一个组件里面的一个设计!(Nacos的服务器客户端模型)
v=3
1
Part-1概念
Part-3原理
上面的推模型有问题,那么我们也很容易想到二个方案:client主动拉模型:
set v=3
方案二:
2. 时间事件就是redis服务器将自己需要定期执行的一些动作封装成时间事件,然后处理;那么有哪些是redis服务器需要定期执行的事情呢?这里简单列举记录几个: 1) redis作为一个服务器运行,肯定要监控自己的各项指标吧,比如内存,键值对数量,过期键的数量等各种乱七八糟的信息; 2)redis的键可以设置过期时间,那redis肯定会定期清理过期键吧; 3)redis能打败memcache的一个很重要的特性就是持久化,redis持久化有两种方式,rdb和aof。对于rdb来说,save肯定是不用关心,也没人会去自断双脚执行save阻塞服务器,但是bgsave就不一样了,有些运维会在需要的时候手动执行bgsave进行rdb备份,当然redis自己也会判断在某些条件触发的时候进行bgsave,那什么时候判断呢?其实会一直判断~等下细说。对于aof来说,一般我们的配置是每秒钟进行一次刷盘操作。这个每秒一次的周期性动作在redis-server中也是一个时间事件来着。 4)别忘了,redis是一个高性能分布式缓存系统,重点是高性能分布式,而不是缓存系统(因为缓存系统一大堆,但是能玩好分布式且做到高性能的没几个),redis是分布式存储,而分布式存储的最大的障碍就是高可用,说白了就是要数据分片冗余,redis也脱离不了这些,所以就有了主从架构,那么主从之间同步还有心跳是不是也是一个周期性的时间事件呢?当然是!
1. set v=3
2
时间事件里面有个很巧妙的设计,也是本次记录的重点,给我们在以后软件设计中提供一个思路!
提个问题:有这么一个任务,需要由服务端开始,只要更新数据,所有的客户端就像也立刻更新自己的数据保持一致。怎么玩?
第一个问题,看上面的伪代码,如果没有文件事件的话,不是会一直阻塞吗?那定时任务怎么定时执行呢?扩展下真实的伪代码:timeout = getNextTimeEventInterval();processFileEvent(timeout);这里先获取下个到来的时间时间的间隔,1. 如果timeout<0,说明时间事件已经到来了,处理命令的函数如果有命令来了,就直接处理完后立刻处理时间事件,如果没有时间事件,不等待,直接进入时间事件!2. 如果timeout>0,说明等下有时间事件,那么处理命令的函数就最多等这个时间间隔,如果期间没有事,就直接返回先处理时间事件,然后进度下次循环,如果期间有命令来了,就执行命令然后continue (真特么巧妙!)
方案三:
第二个问题,如果时间事件耗时很久岂不是耽误server接收读写事件?比较服务客户端才是最重要的呀!问的好,所以redis如果需要处理一些很耗时的时间事件,会fork出子进程进行,并且限制死了timeEvent的执行事件,如果超时就结束。
0 条评论
下一页