在 Redis 完成初始化、创建事件循环后,就会处于等待和处理事件的状态:无限循环aeProcessEvents()函数 。
这个函数在ae.c中实现,该文件主要负责事件循环的实现,在aeProcessEvents()中具体做了几个事情:
- 调用IO多路复用函数(select, epoll, evport, kqueue中的其中一种),阻塞等待事件变成就绪状态或者直到超时,如果有事件就绪,就会将相应事件加入到eventLoop的待处理事件队列 eventLoop->fired 中,然后进入下一个循环 。
- 如果在上一步中,发现有numevents个事件被触发,就会将就绪队列的事件一个个按顺序进行处理,处理的函数为
- 最后一步:如果有时间事件,则进行时间事件的处理:
三、一次命令操作的完整流程
本章是建立在 Redis 已经完成了初始化工作,主要是创建事件循环之后,Redis 接受一个客户端操作的完整流程的介绍 。如果对初始化过程还有问题,请参考上文 。
本章主要分为两个阶段:
- 第一个阶段:一个外部客户端与 Redis 服务器建立 TCP 连接 。
- 比如我们常用的 Telnet 到 Redis 端口的操作 。
- 第二阶段:已经建立连接的客户端,对Redis 发起一次SET命令的操作 。
如图,展示一个新的外部客户端与 Redis 服务器建立连接的过程 。

文章插图
当有客户端连接到 Redis 服务器的时候,注册在事件循环中的监听服务端口的事件就会变成读就绪状态,从而触发这个事件到待处理事件队列中,准备调用acceptTcpHandler进行处理 。
- 为在服务器端创建一个对应本次连接的套接字 。
- 把服务端套接字的文件描述符cfd作为参数,创建client变量 。
- 为该客户端连接创建并注册一个关联了readQueryFromClient处理器的可读事件到事件循环,用于下一步接收并执行命令的工作 。
如图展示一个客户端已经完成了连接,对 Redis 服务器发起一次SET操作后,Redis 处理命令的完整流程 。

文章插图
在上一节中提到,当一个客户端建立连接后,会有一个可读事件关联到事件循环,等待接收命令 。当有客户端发起一次命令操作后,Redis 就会调用readQueryFromClient处理器,对用户发送过来的请求,按 RESP (REdis Serialization Protocol) 进行解析处理后,调用相关的命令进行处理 。
- 调用命令的函数主要做两个事情:(1)查找对应的命令,比如这里的SET(2)调用该命令关联的函数进行处理,这里就是setCommand 。
- setCommand函数将客户端传进来的参数,变更数据库对应 KEY 的值,然后回复客户端 。
- 回复客户端addReply函数将返回给客户端的内容,写到客户端变量的输出缓冲client.buf中,等待发送给客户端 。
以上是整个SET命令的事件处理,不过在这个时候,返回给用户的回复内容,只存放于服务器的客户端变量输出缓冲中 。至于将结果返回给用户的过程,取决于版本,有不同的操作 。
在 4.0 以前,每次的addReply操作会创建一个写事件,然后放到事件循环中执行 。
而 4.0 开始,在每次重新进入一个新的循环之前,就是eventLoop->beforesleep();这个操作,Redis 会尝试直接发送给客户端,只有当发送的内容超过一定大小,无法一次发送完成的时候,才会去创建一个可写事件 。
有兴趣的读者可以去看下 Redis 作者的这个 commit:
antirez in commit 1c7d87d: Avoid installing the client write handler when possible.
推荐阅读
- 关于redis学会这8点就够了
- Redis实现统计网站访问人数的功能
- 利用Redis黑进目标系统
- 抖音短视频的核心是什么?
- Redis混合存储产品与架构介绍
- MAC与IP不得不说的故事--深入ARP工作原理
- redis解析:缓存及常见问题
- 一文弄懂视频网站CDN的访问调度原理
- 设计规范篇 苹果手机拆机与原理图对比分析
- 点点滴滴学5G——一文读懂5G中BWP的原理
