Redis源码 - 事件管理
Redis 的事件分类
| 分类 | 描述 |
| 定时器 | 线程内定时响应,更新缓存时间、关闭非活动的客户端连接等等 |
| pipe | 线程间通信,用于其他线程通知主线程退出aeApiPoll() |
| unixsocket | 本地进程间通信,当client和redis部署在一台机器上时,使用unixsocket性能会更高 |
| TCP | 网络或进程间通信 |
Redis支持4种I/O模型:evport、epoll、kqueue、select,通过宏定义决定使用某一个
| 事件处理 | 描述 | 接口函数名称 | 模型实现 | |||
| select | kqueue | epoll | evport | |||
| aeCreateEventLoop() | 创建I/O模型句柄 | aeApiCreate() | kqueue() | epoll_create() | port_create() | |
| aeCreateFileEvent() | 注册事件并指定回调函数 | aeApiAddEvent() | FD_SET() | kevent() | epoll_ctl() | port_associate() |
| aeDeleteFileEvent() | 删除事件 | aeApiDelEvent() | FD_CLR() | kevent() | epoll_ctl() | port_dissociate() |
| aeProcessEvents() | 事件循环 | aeApiPoll() | select() | kevent() | epoll_wait() | port_getn() |
1 配置加载
config.c : void loadServerConfig(char *filename, char *options)
加载配置文件端口
else if (!strcasecmp(argv[],"port") && argc == ) {
server.port = atoi(argv[]);
if (server.port < || server.port > ) {
err = "Invalid port"; goto loaderr;
}
加载配置文件绑定的网卡地址
else if (!strcasecmp(argv[],"bind") && argc >= ) {
int j, addresses = argc-;
if (addresses > CONFIG_BINDADDR_MAX) {
err = "Too many bind addresses specified"; goto loaderr;
}
for (j = ; j < addresses; j++)
server.bindaddr[j] = zstrdup(argv[j+]);
server.bindaddr_count = addresses;
}
加载配置文件最大连接数量
else if (!strcasecmp(argv[],"maxclients") && argc == ) {
server.maxclients = atoi(argv[]);
if (server.maxclients < ) {
err = "Invalid max clients limit"; goto loaderr;
}
}
2 创建I/O模型句柄
server.c : void initServer(void)
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
if (server.el == NULL) {
serverLog(LL_WARNING,
"Failed creating the event loop. Error message: '%s'",
strerror(errno));
exit();
}
3 监听连接
根据配置文件port、bind字段设置的内容监听IPv4、IPv6的地址,若未指定,则监听IP:0.0.0.0
server.c : void initServer(void)
if (server.port != &&
listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
exit() if (server.unixsocket != NULL) {
unlink(server.unixsocket); /* don't care if this fails */
server.sofd = anetUnixServer(server.neterr,server.unixsocket,
server.unixsocketperm, server.tcp_backlog);
if (server.sofd == ANET_ERR) {
serverLog(LL_WARNING, "Opening Unix socket: %s", server.neterr);
exit();
}
anetNonBlock(NULL,server.sofd);
}
listenToPort()返回创建的I/O句柄和数量。
unixsocket 是用于本地进程间通信的
4 注册事件
server.c : void initServer(void)
4. 1 注册定时器事件
指定回调函数 serverCron() ,该函数用于处理时间类逻辑,比如将长时间无数据到来的客户端连接关闭。
if (aeCreateTimeEvent(server.el, , serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.");
exit();
}
4.2 注册I/O型事件
注册TCP 连接、unixsockt 连接、pipe 读等事件。前两者是在有新连接到来时响应,用来接受连接并通过aeCreateFileEvent()注册数据读写事件;后者pipe的读事件用于其他线程通知本线程退出aeApiPoll(),所以其回调函数可以什么都不做,只是个空函数。
for (j = ; j < server.ipfd_count; j++) {
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR)
{
serverPanic(
"Unrecoverable error creating server.ipfd file event.");
}
}
if (server.sofd > && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event.");
if (aeCreateFileEvent(server.el, server.module_blocked_pipe[], AE_READABLE,
moduleBlockedClientPipeReadable,NULL) == AE_ERR) {
serverPanic(
"Error registering the readable event for the module "
"blocked clients subsystem.");
}
5 事件循环及处理
ae.c
启动事件循环,就是个while死循环,循环调用事件处理函数:aeProcessEvents()
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = ;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}
事件处理主要干的几件事
- 查找距离最近的要响应的定时器,计算相隔时间
- aeApiPoll 检测 I/O 事件,设定超时时间为第1步计算得到的时间
- 对于有事件到来的 I/O,调用注册时指定的回调函数
- 对于第1步找到定时器,调用注册时指定的回调函数
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
int processed = , numevents; if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return ; if (eventLoop->maxfd != - ||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j;
aeTimeEvent *shortest = NULL;
struct timeval tv, *tvp; if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
shortest = aeSearchNearestTimer(eventLoop);
if (shortest) {
//...
} else {
//...
} numevents = aeApiPoll(eventLoop, tvp); if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
eventLoop->aftersleep(eventLoop); for (j = ; j < numevents; j++) {
//...
}
} if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop); return processed;
}
Redis源码 - 事件管理的更多相关文章
- Redis源码阅读(一)事件机制
Redis源码阅读(一)事件机制 Redis作为一款NoSQL非关系内存数据库,具有很高的读写性能,且原生支持的数据类型丰富,被广泛的作为缓存.分布式数据库.消息队列等应用.此外Redis还有许多高可 ...
- Redis 源码简洁剖析 10 - aeEventLoop 及事件
aeEventLoop IO 事件处理 IO 事件创建 读事件处理 写事件处理 时间事件处理 时间事件定义 时间事件创建 时间事件回调函数 时间事件的触发处理 参考链接 Redis 源码简洁剖析系列 ...
- Redis源码分析:serverCron - redis源码笔记
[redis源码分析]http://blog.csdn.net/column/details/redis-source.html Redis源代码重要目录 dict.c:也是很重要的两个文件,主要 ...
- Redis 源码简洁剖析 07 - main 函数启动
前言 问题 阶段 1:基本初始化 阶段 2:检查哨兵模式,执行 RDB 或 AOF 检测 阶段 3:运行参数解析 阶段 4:初始化 server 资源管理 初始化数据库 创建事件驱动框架 阶段 5:执 ...
- Redis源码漂流记(二)-搭建Redis调试环境
Redis源码漂流记(二)-搭建Redis调试环境 一.目标 搭建Redis调试环境 简要理解Redis命令运转流程 二.前提 1.有一些c知识简单基础(变量命名.常用数据类型.指针等) 可以参考这篇 ...
- 一起学习redis源码
redis的一些介绍,麻烦阅读前面的几篇文章,想对redis的详细实现有所了解,强力推荐<redis设计与实现>(不仅仅从作者那儿学习到redis的实现,还有项目的管理.思想等,作者可能比 ...
- redis源码笔记(一) —— 从redis的启动到command的分发
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...
- Redis源码学习:字符串
Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串, ...
- Redis源码剖析和注释(七)--- 快速列表(quicklist)
Redis 快速列表(quicklist)1. 介绍quicklist结构是在redis 3.2版本中新加的数据结构,用在列表的底层实现. 通过列表键查看一下:redis 列表键命令详解 127.0. ...
随机推荐
- 阿里云OCR图片转换成文字识别调用
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Drawing; using S ...
- java网络爬虫基础学习(三)
尝试直接请求URL获取资源 豆瓣电影 https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort= ...
- java8 list转map,list集合中的元素的属性转set,list集合中对象的属性转list
一.使用java8对list操作 1.1list转map private Map<String, Member> getMemberMap() { List<Member> m ...
- 面试7家,收到5个offer,我的Python就业经验总结 !
*---------------------------------------人生处处有惊喜,背后却是无尽的辛酸苦辣. Python找工作并不容易,老表面试了很多企业,总结了些宝贵经验! 一周转 ...
- QinQ 简介
QinQ 是一种二层隧道协议,通过将用户的私网报文封装上外层 VLAN Tag,使其携带两层 VLAN Tag 穿越公网,从而为用户提供了一种比较简单的二层VPN隧道技术.QinQ 的实现方式可分为两 ...
- Swing 混合布局
案例一:Border边境边界 package swing; /** * swing 混合布局 */ import java.awt.*; import javax.swing.*; public cl ...
- scala的多种集合的使用(6)之映射Map的操作方法
1.创建映射 1)创建不可变的映射 scala> val status = Map(1 -> "a",2 -> "b") status: sc ...
- Bool的转录功能
Bool的转录功能 and 当and前边的条件成立时,才执行后边的条件.当前边的条件不成立时,后边的条件一定执行. 特性实用: 不使用if.while写一个具有判断功能的代码. def fun(): ...
- Vue—组件传值及vuex的使用
一.父子组件之间的传值 1.父组件向子组件传值: 子组件在props中创建一个属性,用以接收父组件传来的值 父组件中注册子组件 在子组件标签中添加子组件props中创建的属性 把需要传给子组件的值赋给 ...
- OO第一单元总结——多项式求导
第一次作业分析 1.程序结构分析 类图: 好吧,这一次基本上完全是在面向过程编程,没有看出来任何的面向对象的特性. 复杂度: 可以看到模块间的相互耦合度很高,PolyDerive方法的非结构化程度也不 ...