一、多路复用
多路”指的是多个网络连接,“复用”指的是复用同一个线程
![](https://github.com/AlgoricLi/MyImage/blob/master/Redis/%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8.png?raw=true)
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作
redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间
二、Redis事件
Redis服务器是一个事件驱动程序,主要处理以下两类事件:
- 文件事件:文件事件其实就是对Socket操作的抽象,Redis服务器与Redis客户端的通信会产生文件事件,服务器通过监听并处理这些事件来完成一系列的网络操作
- 时间事件:时间事件其实就是对定时操作的抽象,前面我们已经讲了RDB、AOF、定时删除键这些操作都可以由服务端去定时或者周期去完成,底层就是通过触发时间事件来实现的
2.1 文件事件
文件事件是对套接字操作的抽象, 每当一个套接字准备好执行连接应答(accept)、写入、读取、关闭等操作时, 就会产生一个文件事件。 因为一个服务器通常会连接多个套接字, 所以多个文件事件有可能会并发地出现
Redis 基于 Reactor 模式开发了自己的事件处理器
![](https://github.com/AlgoricLi/MyImage/blob/master/Redis/Redis%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8.png?raw=true)
在Redis 中将感兴趣的事件及类型(读、写)通过IO多路复用程序注册到内核中并监听每个事件是否发生。当IO多路复用程序返回的时候,如果有事件发生,redis在封装IO多路复用程序时,将所有已经发生的事件及该事件的类型封装为aeFiredEvent类型,放到aeEventLoop的fired成员中,形成一个队列
尽管多个文件事件可能会并发地出现, 但 I/O 多路复用程序还是会将所有产生事件的套接字都入队到一个队列里面, 然后通过这个队列, 以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字: 当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O 多路复用程序才会继续向文件事件分派器传送下一个套接字.
Redis 客户端与服务器进行连接并发送命令的整个过程
![](https://github.com/AlgoricLi/MyImage/blob/master/Redis/%E6%96%87%E4%BB%B6%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E5%99%A8.png?raw=true)
- Redis 客户端向服务器发起连接
- 监听套接字将产生
AE_READABLE
事件, 触发连接应答处理器执行: 处理器会对客户端的连接请求进行应答, 然后创建客户端套接字, 以及客户端状态, 并将客户端套接字的AE_READABLE
事件与命令请求处理器进行关联, 使得客户端可以向主服务器发送命令请求 - 客户端向主服务器发送一个命令请求, 那么客户端套接字将产生
AE_READABLE
事件,发命令请求处理器执行, 处理器读取客户端的命令内容, 然后传给相关程序去执行 - 执行命令将产生相应的命令回复, 为了将这些命令回复传送回客户端, 服务器会将客户端套接字的
AE_WRITABLE
事件与命令回复处理器进行关联: 当客户端尝试读取命令回复的时候, 客户端套接字将产生AE_WRITABLE
事件, 触发命令回复处理器执行, 当命令回复处理器将命令回复全部写入到套接字之后, 服务器就会解除客户端套接字的AE_WRITABLE
事件与命令回复处理器之间的关联
2.2 时间事件
持续运行的Redis服务器会定期对自身的资源和状态进行检查和调整,这些定期的操作由serverCron函数负责执行,它的主要工作包括:
- 更新服务器的统计信息(时间、内存占用、数据库占用)
- 清理数据库的过期键值对
- AOF、RDB持久化
- 如果是主从服务器,对从服务器进行定期同步
- 如果是集群模式,对进群进行定期同步和连接
Redis服务器将时间事件放在一个链表中,当时间事件执行器运行时,会遍历整个链表。时间事件包括:
- 周期性事件(Redis一般只执行serverCron时间事件,serverCron时间事件是周期性的)
- 定时事件
执行思路:
- 记录最新一次执行这个函数的时间,用于处理系统时间被修改产生的问题。
- 遍历链表找出所有 when_sec 和 when_ms 小于现在时间的事件。
- 执行事件对应的处理函数。
- 检查事件类型,如果是周期事件则刷新该事件下一次的执行事件。
- 否则从列表中删除事件