《Redis官方文档》Redis事件库

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介:

原文链接 译者:cndpzc

Redis实现了自己的事件库,代码在ae.c中。想要理解Redis事件库的工作原理,最好的方法就是去理解Redis如何使用它。

事件循环初始化

redis.c中的initServer函数初始化了redisServer结构体变量的众多成员,其中一个就是Redis事件循环(event loop)el


1 aeEventLoop *el 

initServer调用aeCreateEventLoop(定义在ae.c)初始化server.el的成员。aeEventLoop的定义如下:


01 typedef struct aeEventLoop 
02 { 
03     int maxfd; 
04     long long timeEventNextId; 
05     aeFileEvent events[AE_SETSIZE]; /* 已经注册的事件 */ 
06     aeFiredEvent fired[AE_SETSIZE]; /* 已经就绪的事件 */ 
07     aeTimeEvent *timeEventHead; 
08     int stop; 
09     void *apidata; /* 这是polling API使用的专有数据 */ 
10     aeBeforeSleepProc *beforesleep; 
11 } aeEventLoop; 

aeCreateEventLoop

aeCreateEventLoop首先为aeEventLoop结构体分配内存,然后调用ae_epoll.c:aeApiCreate
aeApiCreate分配aeApiState的空间,它有两个成员:epfd保存epoll_create调用返回的epoll文件描述符,events是Linux epoll库中定义的epoll_event结构类型。后面会再介绍events的使用。
接下来是ae.c:aeCreateTimeEvent,但是在那之前,initServer会先调用anet.c:anetTcpServer创建一个监听描述符(listening descriptor),默认监听6379端口。返回的监听描述符保存在server.fd

aeCreateTimeEvent

aeCreateTimeEvent接收如下参数:

  • eventLoop:即redis.c中的 server.el
  • milliseconds:从当前时间开始距离定时器过期的毫秒数。
  • proc:函数指针,保存了定时器过期后调用的函数地址。
  • clientData: 通常是NULL
  • finalizerProc:指向定时事件被移除前要调用的函数。

initServer调用aeCreateTimeEventserver.el中的timeEventHead成员添加一个定时事件,timeEventHead是指向定时事件链表的指针。如下是 redis.c:initServer函数中调用aeCreateTimeEvent的代码。


1 aeCreateTimeEvent(server.el /*eventLoop*/, 1 /*milliseconds*/, serverCron /*proc*/, NULL /*clientData*/, NULL /*finalizerProc*/); 

redis.c:serverCron执行很多后台操作来保持Redis正常运转。

aeCreateFileEvent

aeCreateFileEvent函数实质就是执行epoll_ctl系统调用,以将anetTcpServer创建的监听描述符增加到EPOLLIN事件队列,并将它和aeCreateEventLoop创建的epoll描述符相关联。
下面解释了 redis.c:initServer中调用aeCreateFileEvent具体做的工作。
initServer传递了如下参数给aeCreateFileEvent

  • server.elaeCreateEventLoop创建的事件循环,epoll描述符是从server.el里获取的。
  • server.fd监听描述符,作为从eventLoop->events中获取相关文件事件结构体的索引,结构体中存储了回调函数等信息。
  • AE_READABLE:表示必须监视server.fdEPOLLIN事件。
  • acceptHandler:当被监听的事件就绪时执行的函数,函数指针存储在eventLoop->events[server.fd]->rfileProc

以上完成了Redis事件循环的初始化。

事件循环的处理

redis.c:main通过调用ae.c:aeMain来处理前一阶段初始化好的事件循环。
ae.c:aeMain在一个while循环中调用ae.c:aeProcessEvents来处理就绪的定时事件和文件事件。

aeProcessEvents

ae.c:aeProcessEvents在事件循环上调用ae.c:aeSearchNearestTimer寻找最先要过期的定时事件。我们的示例中,事件循环里只有ae.c:aeCreateTimeEvent创建的一个定时事件(译者注:即前面调用ae.c:aeCreateTimeEvent使用的回调redis.c:serverCron
请记住,aeCreateTimeEvent创建的定时事件很可能已经过期了,因为过期时间只有1毫秒。定时器过期后,timeval结构体的tvp变量会把成员变量秒和毫秒都重置为0。
tvp结构体变量和事件循环变量作为参数传给了ae_epoll.c:aeApiPoll
aeApiPoll函数在epoll描述符上调用了epoll_wait,然后用下面内容填充 eventLoop->fired数组。

  • fd:准备好做读/写操作的描述符,操作类型取决于mask值。
  • mask:标识读/写事件可以在对应描述符上执行。(译者注:有读事件mask |= AE_READABLE,有写事件mask |= AE_WRITABLE

aeApiPoll返回已就绪事件的个数。来看个实际的例子,假设有客户端发起了连接请求,那么aeApiPoll将会注意到,使用监听描述符填充eventLoop->fired数组的描述符成员,把mask设为AE_READABLE
现在,aeProcessEvents调用redis.c:acceptHandler回调函数。acceptHandler监听描述符上执行accept,返回一个客户端连接描述符redis.c:createClient通过下面这样调用ae.c:aeCreateFileEvent,向连接描述符添加一个文件事件。


1 if (aeCreateFileEvent(server.el, c->fd, AE_READABLE, 
2     readQueryFromClient, c) == AE_ERR) { 
3     freeClient(c); 
4     return NULL; 
5 } 

credisClient结构体变量,c->fd是连接描述符。
然后,ae.c:aeProcessEvent调用ae.c:processTimeEvents

processTimeEvents

ae.processTimeEventseventLoop->timeEventHead开始,依次遍历链表上的定时事件。
对每个过期的定时事件,processTimeEvents调用相应的回调函数。这个示例只会调用唯一注册的定时事件回调函数redis.c:serverCron,回调函数返回的时间表示多少毫秒后它将被再次调用。返回的时间被ae.c:aeAddMilliSeconds记录下来,ae.c:aeMain中的while循环会在下次迭代中继续处理定时事件。
就这些了。 


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
存储 消息中间件 NoSQL
Redis 入门 - C#.NET Core客户端库六种选择
Redis 入门 - C#.NET Core客户端库六种选择
59 8
|
1月前
|
设计模式 NoSQL 网络协议
大数据-48 Redis 通信协议原理RESP 事件处理机制原理 文件事件 时间事件 Reactor多路复用
大数据-48 Redis 通信协议原理RESP 事件处理机制原理 文件事件 时间事件 Reactor多路复用
37 2
|
2月前
|
缓存 NoSQL PHP
使用PHP-redis实现键空间通知监听key失效事件的技术与代码示例
通过上述方法,你可以有效地在PHP中使用Redis来监听键空间通知,特别是针对键失效事件。这可以帮助你更好地管理缓存策略,及时响应键的变化。
92 3
|
5月前
|
存储 NoSQL Java
大事件后端项目34_登录优化----redis_SpringBoot集成redis
大事件后端项目34_登录优化----redis_SpringBoot集成redis
大事件后端项目34_登录优化----redis_SpringBoot集成redis
|
5月前
|
NoSQL Go Redis
如何使用 Go 和 `go-redis/redis` 库连接到 Redis 并执行一些基本操作
如何使用 Go 和 `go-redis/redis` 库连接到 Redis 并执行一些基本操作
80 1
|
5月前
|
NoSQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之如何确保多并发sink同时更新Redis值时,数据能按事件时间有序地更新并且保持一致性
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
4月前
|
Java Redis 数据安全/隐私保护
Redis13的Java客户端-Jedis快速入门,建立连接的写法,ip地址,设置密码密码,选择库的写法
Redis13的Java客户端-Jedis快速入门,建立连接的写法,ip地址,设置密码密码,选择库的写法
|
5月前
|
NoSQL 大数据 Redis
分享5款.NET开源免费的Redis客户端组件库
分享5款.NET开源免费的Redis客户端组件库
|
5月前
|
NoSQL Redis 数据安全/隐私保护
大事件后端项目35——登录优化_redis_主动失效机制实现
大事件后端项目35——登录优化_redis_主动失效机制实现
|
5月前
|
存储 NoSQL Redis
大事件后端项目33_登录优化-redis_思路分析
大事件后端项目33_登录优化-redis_思路分析