深度解析单线程的 Redis 如何做到每秒数万 QPS 的超高处理能力!

兄弟姐妹在线求帮请问下,深度解析单线程的 Redis 如何做到每秒数万 QPS 的超高处理能力!
最新回答
杯酒入怀

2024-10-13 04:26:42

大家好,我是飞哥!


在网络编程中,提到高性能,很多人会想到多线程。但实际上,服务器端仅需单线程便可实现极高处理能力,Redis 就是这一模式的杰出代表,能够支撑每秒数万 QPS 的性能。今天,我们将深入探讨 Redis 核心网络模块的实现,揭示它是如何实现如此高性能的。


本文转自个人技术公众号「开发内功修炼」,关注以获取飞哥最新深度文章。同时,分享我撰写的电子书《理解了实现再谈网络性能》。该书正处于出版流程中。需要电子版的朋友可点击下载,或添加我本人: zhangyanfei748527,获取《理解了实现再谈网络性能》的链接。


一、理解多路复用原理


在开始介绍 Redis 之前,让我们先简单介绍下 epoll。


在传统的同步阻塞网络编程模型中,进程线程的高开销是影响性能的根本原因。单个进程或线程只能处理一个用户请求,犹如一个人只能看管一只羊。当面对成千上万的请求时,这种模式的成本非常高。


性能提升的关键在于让众多请求复用同一个进程或线程,这就是多路复用。多路指的是众多用户连接,复用指的是对进程或线程的高效利用。以羊群为例,只需一名牧羊人即可管理。


实现多路复用需要特殊的 socket 事件管理机制,其中 epoll 是最高效和典型的方案。它的工作原理类似于一只牧羊犬,负责管理与进程或线程复用相关的 socket 事件。


二、Redis 服务启动与初始化


理解了 epoll 的基本原理后,我们将探索 Redis 如何具体实现这一机制。通过 Github 即可获取 Redis 源码,我们关注 5.0.0 版本中的单线程版本实现。


整个 Redis 服务的核心入口位于 src/server.c 文件中,主要集中在 initServer 和 aeMain 函数。在 initServer 这个关键函数内,Redis 执行了以下三项重要操作。


2.1 创建 epoll 对象

在 aeCreateEventLoop 函数中创建 epoll 对象,然后将其保存在 redisServer 的 aeEventLoop 成员中。接下来,我们深入了解 aeCreateEventLoop 的具体逻辑。


在 eventLoop 对象中,eventLoop->events 数组用于保存各种事件处理器。真正创建 epoll 对象的过程发生在 ae_epoll.c 文件的 aeApiCreate 函数中,通过调用 epoll_create 实现。


2.2 绑定监听服务端口

Redis 的 listen 过程发生在 listenToPort 函数中,通过 bind 和 listen 系统调用完成。


Redis 支持多个端口,listenToPort 函数内部使用循环调用 anetTcpServer,逐步展开调用直到执行 bind 和 listen。


2.3 注册事件回调函数

initServer 函数中,Redis 调用 aeCreateEventLoop 创建 epoll 对象后,通过 listenToPort 进行服务端口绑定。接着,调用 aeCreateFileEvent 注册 accept 事件处理器。


在 aeCreateFileEvent 函数中,Redis 为 listen socket 上的新用户连接注册了 acceptTcpHandler 作为读回调函数,负责处理新连接请求。


三、Redis 事件处理循环


在上一节中,我们了解了 Redis 的启动初始化过程。接下来,Redis 将进入 aeMain 开始真正的用户请求处理。


aeMain 函数是一个无休止的循环,每次循环中执行如下关键操作。


3.1 epoll_wait 发现事件

Redis 通过 epoll_wait 统一发现和管理可读(包括 listen socket 上的 accept 事件)、可写事件,并管理 timer 事件。


每当发现特定事件发生,epoll_wait 调用相应注册的事件处理函数进行处理。aeProcessEvents 函数封装了 epoll_wait 的逻辑。


3.2 处理新连接请求

假设现在有新用户连接到达。listen socket 上的 rfileProc 注册的 acceptTcpHandler 负责处理新连接请求。在 acceptTcpHandler 中,主要完成接收连接、创建客户端连接对象和注册读事件处理器。


anetTcpAccept 负责调用 accept 接收新连接,acceptCommonHandler 为连接创建客户端连接对象,createClient 注册读事件处理器。


3.3 处理客户连接上的可读事件

当用户发送命令时,Redis 通过 epoll_wait 发现可读事件后,调用已注册的读处理函数 readQueryFromClient。


在 readQueryFromClient 中,Redis 处理命令并将其转换为内部数据结构,如执行 GET 命令并从内存中查找值。


四、高性能 Redis 网络原理总结


Redis 服务器通过单线程实现高处理能力,每秒可达到数万 QPS,这主要得益于对 Linux 多路复用机制 epoll 的高效利用。


Redis 的核心逻辑集中于 initServer 和 aeMain 两个关键函数,理解这些函数是掌握 Redis 网络原理的基础。


总结 Redis 的网络核心模块,包括启动服务和事件循环处理,对于深入理解网络编程大有裨益。相信通过本文的介绍,您对网络编程的理解将会更加深入。


快来分享这篇文章给您的技术好友吧!


最后,分享我撰写的电子书《理解了实现再谈网络性能》,该书正处于出版流程中。电子版下载地址或添加我本人: zhangyanfei748527,获取《理解了实现再谈网络性能》的链接。