Redis使用IO多路复用进行事件处理机制

一、epoll多路复用

这里重点要说的就是redis的IO编程模型,首先了解下


为什么要有多路复用呢 ?


案例

引用知乎上一个高赞的回答来解释什么是I/O多路复用。假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:


第一种选择:按顺序逐个检查,先检查A,然后是B,之后是C、D....这中间如果有一个学生卡住,全班都会被耽误。这种模式就好比,你用循环挨个处理socket,根本不具有并发能力。


第二种选择:你创建30个分身,每个分身检查一个学生的答案是否正确。 这种类似于为每一个用户创建一个进程或者 - 线程处理连接。


第三种选择,你站在讲台上等,谁解答完谁举手。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理 E 和 A。


如果没有多路复用,一个线程只能监听一个端口的一个连接,这样这个效率比较低。当然我们有几种办法可以破除这个,一个是使用多线程模型,我们还是监听一个端口,

但是一个请求进来,我们为其创建一个线程。但是这种消耗是比较大的。所以我们一直想办法,有没有办法一个线程监听多个端口,或者多个一个端口的多个连接(fd)。


这里再说说fd, 文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,

所有执行I/O操作(包括网络socket操作)的系统调用都通过文件描述符。每个连接请求上来,都会创建一个连接套接字,一个连接使用一个连接套接字。


对于监听端口,我们会有一个监听套接字,对应监听fd。我们所有的监听业务都是从监听这个套接字开始的。


那么如果我一个程序能同时监听多个连接套接字,是不是就很赞了。是的,这就是linux的io多路复用逻辑。但是这么多连接套接字,传递数据等是断断续续的,

A连接接收一个包,B连接再接收一个包,A连接再接收一个包,B连接再接收一个包....如果我等着A连接把包都接收完再处理B,那效率是非常慢的。所以,

这里我们就需要有一个通知机制,让有收到包的时候通知下处理线程。


linux的IO多路复用逻辑主要有三种:select、 poll、 epoll


select

select模型监听的三个事件:读数据事件,写数据事件,异常事件。


使用select模型的步骤如下:

  • 我们确定要监听的监听fd列表

  • 调用select监听所有监听fd,阻塞线程。

  • select只有当有事件出现并且有事件的fd已经等待完毕

  • 如果是创建一个连接事件:

    • 创建一个连接套接字,连接fd

    • 将连接fd和监听fd集合放在一起


如果是一个读写事件:

  • 遍历所有fd,判断是否是准备好的fd

  • 如果是准备好的fd,进行业务读写逻辑


poll模型:


poll传递给内核的是:

  • 监听的fd集合

  • 需要监听的事件类型

  • 实际发生的事件类型


poll的模型逻辑是:

  • 我们确定要监听的监听fd列表

  • 调用poll监听所有监听fd,阻塞线程。

  • poll只有当有事件出现才解除阻塞

  • 如果是创建一个连接事件:

    • 创建一个连接套接字,连接fd

    • 将连接fd和监听fd集合放在一起


如果是一个读写事件:

  • 遍历所有fd,判断是否是有读写事件的fd

  • 如果fd有读写事件,进行业务读写逻辑

posted @ 2024-11-06 15:03  jock_javaEE  阅读(8)  评论(0编辑  收藏  举报