redis网络和事件循环
aeFileEvent和connectionType:
- 前者直接面对fd,较简。 后者抽象度高,上升到conection.
redis服务器是事件驱动程序,需要处理两类事件:
- 文件事件:redis服务器与客户端或者其他服务器进行连接,文件事件是对套接字操作的抽象。通信会产生很多套接字文件事件,服务器监听处理这些来完成网络通信。
- 时间事件:定时操作。
// 事件循环
typedef struct aeEventLoop {
int maxfd; // 目前最大注册fd
int maxsize; // 支持的最大fd-1。即events数组大小。
aeFileEvent* events; // 已注册事件数组。events[0]对应fd为0.
aeFileEvent* fireEvents; // 触发的事件队列。
aeApiState* apiState; // IO多路复用状态
int stop; // 事件循环停止标志
} aeEventLoop;
注册和触发事件:
- 为fd注册事件读/写,events[fd].mask包含 AE_READABLE/AE_WRITABLE, 并且对于fileproc指向回调
- IO复用等待返回填充到fireevtns,如果fd读/写事件触发,fireevents就会有一个{fd, AE_READABLE/AE_WRITABLE}
- 注册事件是静态的,存储所有注册事件
- 触发事件队列是动态,存储当前已触发事件,
文件事件
基于Reactor模式开发了自己的网络事件处理器:file event handler.
- 多路复用,监听多套接字,根据套接字执行任务关联不同事件处理器。
- IO复用ready后,通过关联的处理器进行执行。
事件循环:IO多路复用会把ready的套接字放到队列,逐一送到分派处理,只有处理完成后,才会继续从队列取出处理。
IO多路复用机制
整体分为三阶段:
- 事件注册:将描述符或者事件注册到事件循环。
- 事件等待:使用select、epoll等待事件触发。
- 事件处理:触发事件后,调用注册的回调器处理。
// 事件循环
typedef struct aeEventLoop {
int maxfd; // 目前最大注册fd
int maxsize; // 支持的最大fd。即events数组大小。
aeFileEvent* events; // 已注册事件数组。events[0]对应fd为0.
aeFileEvent* fireEvents; // 触发的事件队列。
} aeEventLoop;
// 注册事件
typedef struct aeFileEvent {
int type; // AE_READABLE,AE_WRITABLE
aeFileProc* rfileProc; // 事件读处理程序
aeFileProc* wfileProc; // 事件写处理程序
void* procArg; // 事件处理程序参数
} aeFileEvent;
// 触发事件
typedef struct aeFireEvent {
int fd;
int type; // 标记events[fd]触发上触发的事件类型。
} aeFireEvent;
时间事件:
- 定时:
- 周期
typedef struct aeTimeEvent {
long long id; // 时间事件id
long when; // 毫秒时间戳
aeTimeProc* timeProc; // 时间事件处理函数
void* procArg; // 时间事件处理函数参数
struct aeTimeEvent* next; // 下一个时间事件
} aeTimeEvent;
id从小到大递增,新事件ID>旧事件ID
定时心跳、info事件 穿插在正常的其他都事件流中, 需要很明确的将其分开。
RESP协议
数据类型 | 前缀 | 示例 |
---|---|---|
简单字符串 | + |
`+OK |
错误信息 | - |
-ERR unknown command |
整数 | : | :100 |
批量字符串 | $ | $5\r\nhello\r\n |
多条批量字符串(数组 | * | *2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n |
RESP 设计简单高效,便于解析,同时支持二进制安全的数据传输。
【问题】
- 为什么RESP没有全局起止符号?这在RESP+RDB这样的内容下还要解析,而非直接获取。
优点:如果是这样,RESP格式内容边界很清晰。能够完整获取一个RESP内容。
<START>+OK\r\n<END>
<START>*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n<END>
<START>+FULLRESYNC repl-id offset\r\n$1024\r\n<RDB data><END>
缺点:
- 冗余开销:每个消息多12个字节,对于高频SET GET 命令性能影响。
- 不符合TCP流式传输:需要等待完整一个RESP消息传来。
{ping}
-> *1\r\n$4\r\nping\r\n
服务器
多类型监听是什么,为什么?一种类型需要多端口监听吗?
- TCP 6379; TLS 6380; Unix socket
/tmp/redis.sock
- 同一类型如tcp通过多路复用即可搞定大并发,所以不需要。
## 套接字
EAGAIN
描述符是非阻塞模式,当前操作请等候。 表示 写缓冲满或读缓冲空
EINTR
阻塞操作被信号打断。