Redis通过使用I/O多路复用程序来监听过个socket,文件事件处理器既实现高性能网络通讯模型,又可以很好的与Redis以单线程运行的模块进行对接,保持了Redis内部单线程设计的简单性。

文件事件处理器的四个部分:套接字、I/O多路复用程序、文件事件分发器、事件处理器

套接字socket

  • 文件事件就是对套接字的抽象,每当一个套接字准备好执行连接、写入、读取、关闭等操作时,都会产生一个文件事件。
  • 因为一个Redis服务器会有多个客户端连接,也就是说有多个套接字,所以多个文件事件可能会并发出现。

I/O多路复用程序

  • I/O多路复用程序,负责监听多个套接字,按照处理顺序,将套接字存放在一个队列中。
  • 套接字队列以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字。
  • 当上一个套接字产生的事件被处理完毕之后, I/O 多路复用程序才会继续向文件事件分派器传送下一个套接字。

Redis的I/O模型主要是基于epoll实现的,不过也提供了select和kquque的实现,默认是采用epoll

一个客户端连接后,将这个文件描述符(connfd)放到一个数组里。

select

select 是操作系统提供的系统调用函数,select 只能监听 1024 个文件描述符,通过它,我们可以把一个文件描述符的数组发给操作系统, 让操作系统去遍历,确定哪个文件描述符可以读写, 然后告诉我们去处理,不过,当 select 函数返回后,用户依然需要遍历刚刚提交给操作系统的 list。

只不过,操作系统会将准备就绪的文件描述符做上标识,用户层将不会再有无意义的系统调用开销。

poll

它和 select 的主要区别就是,去掉了 select 只能监听 1024 个文件描述符的限制。

epoll

epoll 是最终的大 boss,它解决了 select 和 poll 的一些问题。

所以 epoll 主要就是针对这三点进行了改进。

1. 内核中保存一份文件描述符集合,无需用户每次都重新传入,只需告诉内核修改的部分即可。

2. 内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒。

3. 内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合。

具体,操作系统提供了这三个函数。

第一步,创建一个 epoll 句柄

int epoll_create(int size);

 第二步,向内核添加、修改或删除要监控的文件描述符。

int epoll_ctl(  int epfd, int op, int fd, struct epoll_event *event);

第三步,类似发起了 select() 调用

int epoll_wait(  int epfd, struct epoll_event *events, int max events, int timeout);

文件事件分发器

  • 文件事件分派器接受I/O多路复用程序传递的套接字,根据套接字的事件类型,调用相应的事件处理器进行处理。

事件处理器

  • 连接应答处理器:对连接服务器的各个客户端进行应答。
  • 命令请求处理器:接收客户端传来的命令请求。
  • 命令回复处理器:向客户端返回命令的执行结果
  • 复制处理器:当主服务器和从服务器进行复制操作时, 主从服务器都需要关联此处理器。
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐