Redis的线程模型
Redis内部使用文件事件处理器,它是单线程的
同时采用IO复用来监听多个Socket,根据Scoket上的事件来选择对应的事件处理器
文件事件处理器的结构:
多个Socket、IO多路复用程序、 事件分派器
以及事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
事件处理器本质上是一个个的函数,用于定义当某个事件发生时,redis该执行什么动作
当并发出现文件事件时,IO复用程序会把对应的scoket放入队列中,
事件分派器从队列中取出socket,根据事件类型,把socket交给对应的事件处理器
IO多路复用
多路IO复用:使用一个线程来监听多个scoket的就绪状态
作用:避免IO阻塞,提高CPU利用率
请求在准备时,Redis可以一边监视它的状态,一边处理其它请求,
待该请求准备完毕后,才去处理它,避免了阻塞等待
多路:指由多个网络连接
复用:复用同一个线程
多路IO的模式:
1 select:同时监控有限个请求,一个个地去询问请求是否准备完毕,
把准备完成的请求交给redis处理,最多监视1024个请求
2 poll:不限制监视请求的数量
上述方案本质上就是让内核不断地去循环遍历IO状态,
所以只是减少了用户进程发起系统调用的次数,但是内核的工作量并没有减少
3 epoll:不限制数量,也不轮询请求的状态,而是为每个进入注册一个回调函数
当IO准备好时,会通过回调函数来将该IO事件放入队列中
它可以转主动为被动,基于事件驱动和回调
多路复用程序会监听多个Socket,会把合适的socket事件放入队列中,
事件分派器会依次从队列中取出一个事件,把它交给对应的事件处理器处理
事件类型:
1 可读事件: 客户端连接redis、客户端给redis发送命令
2 可写事件:客户端读取redis返回的数据
当同时出现可读事件与可写事件时,优先处理可读事件
文件事件处理器:
1 连接应答处理器
Redis初始化时,会把该处理器与Redis监听socket的可读事件关联,
当用户调用connect函数连接Redis的监听scoket时,
Redis的监听Socket会产生可读事件,然后由连接应答处理器来处理,
并创建客户端Socket
2 命令请求处理器
当客户端与Redis建立连接后,Redis会把客户端socket的可读事件与该处理器关联,
当客户端向Redis发送命令时,客户端Scoket会产生可读事件,
然后命令请求处理器会处理这个事件。
只有客户端与Redis保持连接,命令请求处理器就会一直处理客户端Scoket的可读事件
这两个处理器对应的可读事件的区别:
连接应答处理器处理的可读事件是由Redis的监听Scoket产生的,
而命令请求处理器处理的可读事件由客户端的Socket产生的
3 命令回复处理器:
当Redis准备给客户端返回数据时,
就把客户端Scoket的可读事件与命令回复处理器关联
当客户端准备好接收数据时,会产生可读事件,
由命令回复处理器来处理,
当数据写入完毕后,会解除处理器与事件之间的关联
客户端与Redis的交互过程
1 Redis的监听Socket的可读事件处于监听状态下
2 当客户端尝试连接时,会触发Redis监听Socket的可读事件,
然后由连接请求处理器来生成客户端Socket,
然后将这个socket的可读事件与命令请求处理器关联
3 当客户端给Redis发送命令时,客户端Socket会产生可读事件,
有命令请求处理器来处理
4 Redis执行命令并要返回数据时,会把客户端Socket与命令回复处理器关联
当客户端开始读取数时,客户端Socket会产生可读事件,由命令回复处理器来处理,
在把数据写入到Socket后,处理器与事件会断开联系
时间事件
分类:
定时事件:让一段程序在指定时间后执行一次
周期性事件:让一段程序每隔指定时间就执行一次
属性:
id:服务器为时间事件创建的全局唯一ID,顺序递增
when:时间事件到达时间,毫秒级UNIX时间戳
timeProc:时间事件处理器,一个函数
实现:服务器将时间事件都放在一个无序链表中(不是按时间顺序排序,而是按照ID排序,新产生的时间事件放在链表的表头),每次时间事件执行器运行时,它就遍历整个链表,查找所有已经到达的时间事件,并调用相应的事件处理器。