Redis 事件处理

aeEventLoop是事件模型中最基本的结构体:

typedef struct aeEventLoop {
int maxfd;
long long timeEventNextId;
aeFileEvent events[AE_SETSIZE]; /* Registered events */
aeFiredEvent fired[AE_SETSIZE]; /* Fired events */
aeTimeEvent *timeEventHead;
int stop;
void *apidata; /* This is used for polling API specific data */
aeBeforeSleepProc *beforesleep;
} aeEventLoop;

maxfd标示当前最大的事件描述符。events和fired分别保存了已注册和已注销的FileEvent,AE_SETSIZE是Redis中可以注册的事件的上限,默认为1024*10。
timeEventHead指向一个TimeEvent的链表,timeEventNextId标示下一个定时器。beforesleep是每次事件轮询前都会执行的函数,相当于hook。stop用于停止事件轮询。

 

aeFileEvent可以用于socket事件的监听。mask表示要监听的事件类型,rfileProc和wfileProc分别为读事件和写事件的响应函数。

typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE) */
aeFileProc *rfileProc;
aeFileProc *wfileProc;
void *clientData;
} aeFileEvent;

 aeTimeEvent用于定时器(timer),其实现是一个链表,其中每一个结点是一个timer,并有独立id。when_sec和when_ms指定了定时器的触发时间,timeProc为响应函数,finalizerProc为删除定时器时的“析构函数”。aeFiredEvent表示触发的FileEvent,其中fd为文件描述符,mask为事件类型。

typedef struct aeTimeEvent {
long long id; /* time event identifier. */
long when_sec; /* seconds */
long when_ms; /* milliseconds */
aeTimeProc *timeProc;
aeEventFinalizerProc *finalizerProc;
void *clientData;
struct aeTimeEvent *next;
} aeTimeEvent;

 事件处理:

1、在initServer中,在事件循环aeEventLoop中创建定时器事件,处理函数为serverCron。然后创建文件事件,往socket服务器通道中注册读事件,用于监听客户端的链接。

2、aemain函数中,首先调用Beforesleep。顾名思义,这个方法在Redis每次进入sleep/wait去等待监听的端口发生I/O事件之前被调用。

这个方法做了三件事情:
I.    如果Redi开启了Virtual memory,那么某些clients请求的keys可能因为被swap了,因此这些client会被block住,当这些clients请求的keys又被swap到内存中时,则这些被block住的clients应该unblock,然后被处理;io_ready_clients就是用来维护这些clients的,为了尽快响应client的请求,因此在每次sleep前都先处理这些请求
II.    某些Redis操作是blocking的,如BLPOP,那么执行这些操作的clients可能会被block住,unblocked_clients这个list就是用来维护那些刚被unblock的clients,如果这个list不为空,则也要尽快响应这些clients
III.    flushAppendOnlyFile;因为clients的Socket的write只能在eventLoop里面进行,而flushAppendOnlyFile又是在每次sleep之前被调用,所以在eventLoop里面的所有AOF writes都是先写到内存里的一块buffer里面,flushAppendOnlyFile则负责把这个buffer内容flush到disk;

3、执行完beforesleep后调用aeprocessEvents,该方法主要是处理各种监听到的文件读写事件和到期响应的定时事件,简单介绍下过程:                                     

a)    首先通过遍历eventLoop中注册的timeEvent找出离当前最近timeEvent(即shortest)。
b)    调用epoll_wait()方法,等待I/O事件的发生,这里其实就是等待客户端的连接。 为了尽快响应时间事件,epoll_wait()方法的等待时间为shortest与当前时间的差值,如果该差值小于零,则epoll_wait()轮询至有I/O事件发生;并将有IO事件发生的fd及mask记录到fired中。
c)    响应eventLoop中fired的aeFileEvent,这里调用的就是之前设置的文件处理函数acceptTcpHandler。
d)    响应完I/O事件后,则通过timeEventHead遍历timeEvent,逐一响应timeProc--serverCron。

4、acceptTcpHandler主要是 调用createClient方法创建redisClient,同时注册新的fileEvent(AE_READABLE),并绑定处理函数为readQueryFromClient;readQueryFromClient从指定的Socket中读取client发送过来的数据,并按照Redis的协议(后面将单独介绍)进行解析组装成Redis的各个command,然后通过查找commandTable,执行command。Redis在执行完client请求后的命令后,向Client端return数据,就是往Socket写入数据,这使一个AE_ AE_WRITABLE事件。Redis执行完command后,调用addReply方法,然后在这个方法里面调用installWriteEvent来注册一个AE_WRITABLE事件,并绑定事件处理函数sendReplyToClient,用来把数据发送到client。

5、serverCron的这个函数对Redis的正常运行来说很重要,对于Redis的使用者来说,最重要的就是能够迅速直观地看到Redis的当前的运行状况(keys,sizes,memory等),serverCron就能够使用户得知这些信息,此外,serverCron这个方法定时周期地运行,还承担了AOF Write,VM Swap,BGSAVE,Rehash的操作,使得Redis的运行更加平稳。




 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值