阅读浏览器源码的好工具:SourceGraph
Redis通过将对应事件注册到eventloop中,然后不断循环检测有无事件触发。

aeProcessEvents代表处理一次循环事件处理:
- 取出最近的一次定时器事件。
- 计算该超时定时事件还有多久才可以触发,若已超时则设置t为0,其中t为第3步的超时阈值。
- 调用select或者epoll等待网络事件触发,或者超时。
- 处理触发的各个事件,包括网络事件和超时事件。
也就是说每次循环事件处理中,网络IO读写事件的处理过程中,会顺便检测处理定时事件。
关于第2点的额外说明,每次事件循环处理周期当中,获取距离当前的这个时间间隔值。拿来做什么用呢?
为了避免“忙等待”,我们在检查FD的IO读写状态时(select或者epoll),都会采用阻塞的方式,如果没有可读可写的FD,就一直阻塞着等待。但是,我还有定时器事件要处理啊,如果一直没有IO事件,那我定时器事件不是一直没法处理么?所以,我们会给select或者epoll传入一个阻塞的超时时间,超过这个时间,都给我返回。 下面获取的这个值,就是用于设置阻塞超时时间的。这样做,既可以避免非阻塞式的忙等待,又可以保证定时器事件能够按时得到处理。 其实这种处理方式非常普遍,以C为开发语言的很多服务型软件都是这样玩的。
Redis初始化服务的时候,会先初始化一个Eventloop
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);

目前eventloop支持注册:
- 定时器事件
- 网络IO读写事件
其中网络IO读写事件的fd看做下标的原因是:Linux内核会给每个进程维护一个文件描述符表。
而POSIX标准对于文件描述符进行了以下约束:
- fd为0、1、2分别表示标准输入、标准输出和错误输出。
- 每次新打开的fd,必须使用当前进程中最小可用的文件描述符。
因此注册事件下标能与系统fd形成有效重合复用。
其中我们关注的网络IO事件数据结构如下:
一般情况下,Redis会先处理读事件(AE_READABLE),再处理写事件(AE_WRITABLE)。 这个顺序安排其实也算是一点小优化,先读后写可以让一个请求的处理和回包都是在同一次循环里面,使得请求可以尽快地回包。
网络IO事件注册的时候,除了正常的读写事件外,还可以注册一个AE_BARRIER事件,这个事件就是会影响到先读后写的处理顺序。 如果某个fd的mask包含AE_BARRIER,那它的处理顺序会是先写后读。
网友评论