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有读写事件,进行业务读写逻辑