Redis 核心原理和架构( 二 )


有利于减小高并发量情况下对性能的影响
根据论文 SEDA: An Architecture for Well-Conditioned, Scalable Internet Services 的测试结果显示,相比一个连接分配一个线程的模型,Reactor 模式(固定线程数)在连接数增大的情况下吞吐量不会明显降低,延时也不会也受到显著的影响 。
二、事件循环的 Redis 实现
下面开始,会对 Redis 如何实现事件循环进行说明,会涉及到一些源码的实现部分,如果不感兴趣可以直接跳到第三节看 Redis 怎么利用事件处理模型来处理具体的命令 。
2.1 Redis 事件循环 Event Loop
Redis 的事件循环,最直观的理解,就是一个在不断等待事件的一个无限循环,直到 Redis 程序退出 。
Redis 实现事件循环主要涉及三个源码文件:server.c, ae.c, networking.c 。

  • server.c 的 main()函数是整个 Redis 程序的开始,我们也从这里开始观察 Redis 的行为 。
  • ae.c实现事件循环和事件的相关功能 。
  • networking.c则负责处理网络IO相关的功能 。
 
Redis 核心原理和架构

文章插图
 
 
a. 初始化 Redis 配置
初始化的过程主要做三个事情:
  1. 加载配置
  2. 创建事件循环
  3. 执行事件循环
简化后的代码如下:(跳过不影响理解)
// 0. 定义服务器主要结构体, 加载服务器配置struct redisServer server;initServerConfig();loadServerConfig();// 1. 根据配置参数初始化,initServer() { // 1.1 实际创建事件循环 server.el = aeCreateEventLoop(); // 1.2 为事件循环注册一个可读事件,用于响应外部客户端请求 aeCreateFileEvent(server.el, AE_READABLE, acceptTcpHandler)}// 2. 执行事件循环,等待连接和命令请求aeMain(server.el);初始化过程中被创建的server.el包含了两个事件的列表,它的结构体实现如下:
typedef struct aeEventLoop{ aeFileEvent events[AE_SETSIZE]; /* 注册的事件,被 eventloop 监听 */ aeFiredEvent fired[AE_SETSIZE]; /* 有读写操作需要执行的事件(就绪事件) */} aeEventLoop;b. 创建事件循环
主循环体aeMain()在ae.c文件中被实现,简化后的代码如下:
void aeMain(aeEventLoop *eventLoop) { while (!eventLoop->stop) { aeProcessEvents(eventLoop, AE_ALL_EVENTS); }}事件循环主要就是一个while循环,不断去轮询是否有就绪的事件需要处理,具体的处理函数是aeProcessEvents,接下来会有对这个函数有更详细的介绍 。
c. 创建用于监听端口的事件
在上述 Redis 在初始化时,程序会创建一个关联了acceptTcpHandler处理器的可读事件:
aeCreateFileEvent(server.el, AE_READABLE, acceptTcpHandler)这个可读事件注册到事件循环中,就实现了 Redis 对外提供的服务地址和端口的连接服务 。具体的内容下一个小节事件处理器中介绍 。
2.2 事件处理器 Event Handler
所有事件被创建时,都会关联一个处理器 (handler),并注册到事件循环中,事件处理器用于具体的读写操作 。
Redis 的常用几个事件处理器有:
  • 响应连接的处理器acceptTcpHandler()
  • 读取客户端命令的处理器readQueryFromClient()
  • 返回处理结果的处理器sendReplyToClient()
以上处理器均在networking.c文件下实现,该文件负责 Redis 所有网络 IO 功能的实现 。
一个客户端一次正常的连接和命令操作流程,可以通过上述三个处理器完成 。
Redis 核心原理和架构

文章插图
 
当 Redis 需要监听某个套接字的时候,就会创建一个事件,并注册到事件循环中进行监听,Redis 将处理器以参数的方式关联到事件中 。
比如以下是注册一个可读事件的操作:
aeCreateFileEvent(server.el, fd, AE_READABLE, readQueryFromClient, c)
  • server.el:事件循环 eventloop,一个服务器只有一个el
  • fd:表示这个客户端连接的文件描述符,每个客户端连接对应一个
  • AE_READABLE:表示这是一个可读事件,可以理解为客户端准备进行写操作
  • readQueryFromClient: 这个事件关联的处理器,当事件就绪后,就会调用此处理器
  • c:表示这个客户端在Redis中指向的变量
注册完毕后,事件循环就会将这个事件(套接字)加入到监听的范围,当事件可读时,Redis 就会将这个事件发送到待处理事件队列中等待处理,等到可读就绪时,会被readQueryFromClient处理器处理 。
可以看到整个过程中事件循环和不同处理器之间是解耦的,互不干扰 。这样实现提高了代码的简洁和重用 。
2.3 事件处理 Process Events


推荐阅读