一 整体流程
谈到redis,大家都知道是一种单线程高并发的内存数据库。那么单线程是如何支持高并发的呢?
对于一个内存数据库而言,单纯的内存操作非常的快,其真正的性能瓶颈是在处理网络I/O上。redis采用I/O多路复用的方式来监听多个socket,从而提高其自身的性能。
上图是整个redis网络事件处理的核心流程。I/O多路复用负责监听多个套接字,如果其中某个套接字有事件准备 好,则I/O多路复用程序会将这个准备好的事件放进队列中,文件事件分派器会将不同类型的事件放入不同的事件处理器中进行处理,整个过程是单线程串行的。这里不同的处理器就是不同的函数,它定义了某个事件发生时,服务器应该执行的动作。
二 事件轮询
typedef struct aeEventLoop {
int maxfd; /* highest file descriptor currently registered */
int setsize; /* max number of file descriptors tracked */
long long timeEventNextId;
time_t lastTime; /* Used to detect system clock skew */
aeFileEvent *events; /* Registered events */
aeFiredEvent *fired; /* Fired events */
aeTimeEvent *timeEventHead;
int stop;
void *apidata; /* This is used for polling API specific data */
aeBeforeSleepProc *beforesleep;
} aeEventLoop;上述代码是redis源码中eventloop的定义,我们需要关注两个东西,events和fired
- events即该事件轮训器中注册的事件数组
- fired即为触发的事件数组
事件轮询器会轮询套接字,当其中有注册的事件发生时,即放入fired链表数组中,后续会遍历该数组,进行事件处理
三 时间事件
上述结构体中还有一个是timevent,即为时间事件,这个时间事件即为redis中已注册的定时任务,reids会轮询该链表,当有到期的定时任务时,即执行该任务。
从这点可以看出,无论是文件事件(I/O事件),还是时间事件,系统的调用起点都是在该eventloop中,即所有事件都是由主线程发起。