redis服务器需要处理的事件可以分为两类:
- 文件事件(file event): 套接字的抽象。服务器通过套接字与客户端或其他服务端连接、通信;
- 时间事件(time event): 定时任务。
文件事件
事件模型是基于Reactor开发的:
- 套接字;
- IO多路复用程序;
- 事件分派器;(dispatcher)
- 事件处理器。
每当一个套接字准备好执行:
连接应答(accept)、
写入、
读取、
关闭
等操作时,就会产生一个事件(可能并发)。
IO多路复用程序
:把上游并发的事件组织成一个队列(方便下游单线程地使用)。
当上一个套接字事件被处理完以后,IO多路复用程序才会向事件分派器传送下一个套接字事件。
IO多路复用
具体啥是IO多路复用?就是一个进程处理多个连接。
方案有很多:(详见:http://xiaoyue26.github.io/2017/11/06/2017-11/epoll%E7%9B%B8%E5%85%B3%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/)
- 循环、轮询;
- Select;
- poll;
- epoll(红黑树)/kqueue(哈希表);
- libevent库。
事件优先级
先处理可读,再处理可写。
API接口
1 | 1. createFileEvent(套接字描述符,事件类型,事件处理器):开始监听; |
时间事件
- 定时事件:指定时间后执行1次;
- 周期事件:每隔指定时间就执行1次(总N次)。
时间事件属性:
- id: 全局唯一,递增;
- when: 毫秒,事件到达时间;
- timeProc: 函数,到期执行。
定时事件和周期事件区分:
- timeProc返回
AE_NOMORE
: no more事件,不再调用; - timeProc返回30: 周期事件,30ms后再次调用。
TODO: 全局唯一id=>服务器内唯一?
实现
所有时间事件放在一个无序链表中,每次遍历整个链表,查找所有已到达的时间事件。
性能
由于时间事件很少(1,2个),所以虽然实现很naive,性能也还行。
现有时间事件
serverCron
:(每100ms)
- 更新统计信息:时间、内存、数据库占用;
- 清理过期KV;
- 关闭清理失效客户端;
- AOF\RDB持久化;
- 主从同步;
- 集群模式:定期同步、连接测试。
事件循环
事件无抢占。
先文件事件后时间事件,因此时间事件一般会滞后一点。