大数据-48 Redis 深入理解Redis通信机制:从RESP协议到Reactor事件驱动模型

96 阅读9分钟

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

AI炼丹日志-30-新发布【1T 万亿】参数量大模型!Kimi‑K2开源大模型解读与实践,持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年07月21日更新到: Java-77 深入浅出 RPC Dubbo 负载均衡全解析:策略、配置与自定义实现实战 MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈! 大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

Redis采用单线程+多路复用架构,通过事件驱动实现高并发处理。通信基于RESP协议,定义了简单字符串、错误、整数、批量字符串和数组五种数据格式,具有可读性强、易解析、支持二进制等优点。通信默认使用TCP长连接,支持串行和双工模式,其中Pipeline机制可显著提升吞吐量。Redis内部使用Reactor模式处理IO事件,通过aeEventLoop统一管理网络事件和时间事件,底层利用epoll或kqueue实现高效的IO多路复用,避免多线程带来的锁竞争和上下文切换开销。整体设计简单高效,是其高性能关键所在。

请添加图片描述

章节内容

上节我们完成了:

  • Redis的缓存机制
  • Redis的淘汰策略
  • LRU LFU 等机制

在这里插入图片描述

通信协议

Redis的单线程模型

Redis采用单进程+单线程的架构设计,这种设计有以下特点:

  1. 所有操作都是原子性执行的,避免了多线程环境下的锁竞争问题
  2. 通过I/O多路复用技术处理大量并发连接
  3. 单线程简化了数据结构的实现,提高了性能

Redis协议(RESP)

Redis Serialization Protocol (RESP)是应用系统与Redis服务端交互的通信协议,主要特点包括:

协议格式

RESP定义了5种数据类型:

  1. 简单字符串(Simple Strings):以"+"开头,如 "+OK\r\n"
  2. 错误(Errors):以"-"开头,如 "-ERR unknown command\r\n"
  3. 整数(Integers):以":"开头,如 ":1000\r\n"
  4. 批量字符串(Bulk Strings):以""开头,如""开头,如 "6\r\nfoobar\r\n"
  5. 数组(Arrays):以"*"开头,如 "*2\r\n3\nfoo˚\n˚3\r\nfoo\r\n3\r\nbar\r\n"

通信示例

客户端发送命令:

*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n

服务端响应:

+OK\r\n

应用场景

  1. 客户端库实现:各种语言的Redis客户端库都需要实现RESP协议
  2. 命令行交互:redis-cli工具通过RESP与服务器通信
  3. 主从复制:Redis集群中的主从节点通过RESP协议同步数据

协议优势

  1. 简单易实现
  2. 人类可读
  3. 高效解析
  4. 支持二进制安全的数据传输

响应模式

概念介绍

Redis 协议位于 TCP 层上,即客户端和Redis实例保持双工的连接。 在这里插入图片描述

串行模式

串行模式是一种最基本的客户端-服务器通信方式,其工作流程如下:

  1. 连接建立阶段

    • 客户端与服务端通过TCP/IP协议建立持久的长连接
    • 连接建立后,双方通过心跳机制(ping-pong)保持连接活性
    • 典型的心跳间隔通常为30-60秒,通过ACK应答确认连接状态
  2. 请求-响应流程

    • 客户端发送第一个请求报文
    • 服务端处理该请求并返回响应
    • 客户端必须完整接收到第一个响应后,才能发起第二个请求
    • 每个请求都需要等待前一个请求完成才能继续
  3. 典型应用场景

    • Telnet协议:用于远程终端连接的标准协议
    • Redis-cli工具:Redis数据库的默认命令行接口
    • 简单的RPC调用场景
  4. 性能特点

    • 网络延迟显著影响总吞吐量(RTT时间累加)
    • 无法充分利用网络带宽(存在大量空闲等待时间)
    • 服务端资源利用率低(处理请求时存在等待间隔)
    • 实现简单,无需考虑请求乱序问题
  5. 改进方向

    • 引入管道化(Pipeline)技术
    • 采用多路复用(Multiplexing)方案
    • 实现并行请求处理

这种模式虽然简单可靠,但因其串行处理的特性,在现代高并发场景中通常不是最优选择。

在这里插入图片描述

双工模式

双工模式是指通信双方可以同时进行双向数据传输的工作方式。在计算机网络中,TCP协议就是一种典型的全双工通信协议。

批量请求与批量响应

在双工模式下,系统支持:

  1. 客户端可以一次性发送多个请求
  2. 服务器可以一次性返回多个响应
  3. 请求和响应可以交叉进行而不会混淆(得益于TCP的双工特性)

Pipline技术详解

Pipline(管道)是一种优化网络通信性能的重要技术:

工作原理:

  1. 客户端将多个命令打包成一个批次
  2. 通过单个网络调用发送给服务器
  3. 服务器按顺序执行这些命令
  4. 将执行结果按顺序打包后返回

性能优势:

  • 减少网络往返时间(RTT)
  • 提高吞吐量
  • 降低网络延迟影响

技术特点:

  • 一次pipline操作包含:
    • 多条命令的批量发送
    • 仅需一次网络传输时间
    • 命令执行结果的批量返回

应用场景:

  1. 数据库批量操作
  2. 消息队列的批量生产/消费
  3. 缓存系统的批量读取/写入

示例: 在Redis中,使用pipline可以显著提高批量操作的效率:

# 不使用pipline
for i in 1..100
  redis.set("key#{i}", "value#{i}")
end

# 使用pipline
redis.pipelined do
  100.times do |i|
    redis.set("key#{i}", "value#{i}")
  end
end

后者只需一次网络往返即可完成100次set操作。

我们使用Jedis库可以很轻松的使用 pipline:

Jedis redis = new Jedis("h121.wzk.icu", 6379);
redis.auth("111111");
Pipeline pipe = jedis.pipelined();
for (int i = 0; i <50000; i++) {
    pipe.set("key_"+String.valueOf(i),String.valueOf(i));
}
// 将封装后一次性发给redis
pipe.sync();

Redis通信协议详解

RESP协议概述

Redis客户端与服务器之间的交互采用REdis Serialization Protocol(RESP)序列化协议。这是一种简单高效的二进制安全协议,专为Redis设计,具有以下特点:

请求格式

请求以字符串数组的形式表示要执行的命令及其参数:

*<参数数量>\r\n$<参数1长度>\r\n<参数1数据>\r\n$<参数2长度>\r\n<参数2数据>\r\n...

响应格式

Redis使用以下特有数据类型作为回复:

  1. 简单字符串(Simple Strings):以"+"开头
  2. 错误(Errors):以"-"开头
  3. 整数(Integers):以":"开头
  4. 批量字符串(Bulk Strings):以"$"开头
  5. 数组(Arrays):以"*"开头

通信机制

连接方式

  • 采用TCP协议建立连接
  • 默认服务端口号:6379(可以通过配置修改)
  • 支持TLS加密连接(需配置)

数据分隔

  • 每条命令和数据都必须以CRLF(\r\n)结尾
  • 示例:SET key value命令实际传输为*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n

二进制安全特性

  • 所有参数都是二进制安全的,可以包含任意字节
  • 支持处理包含null字节(\x00)的数据
  • 字符串长度按字节计算而非字符

典型交互示例

命令发送示例

发送GET counter命令:

   *2\r\n
   $3\r\n
   GET\r\n
   $7\r\n
   counter\r\n

发送SET message "Hello World"命令:

   *3\r\n
   $3\r\n
   SET\r\n
   $7\r\n
   message\r\n
   $11\r\n
   Hello World\r\n

响应示例

成功响应:

   +OK\r\n
  1. 错误响应:
   -ERR unknown command 'FOO'\r\n
  1. 整数响应:
   :100\r\n
  1. 批量字符串响应:
   $11\r\n
   Hello World\r\n

性能优化

  • 管道机制(pipelining):允许一次性发送多个命令而不需要等待每个回复
  • 连接复用:保持长连接减少TCP握手开销
  • 批量操作命令:如MSET、MGET等减少网络往返次数

内联格式

可以使用 telnet 工具进行测试,发送一些内容过去

telnet h121.wzk.icu 6379

xxx
xxx
xxx

处理流程

处理流程

  • 服务器启动监听
  • 接受命令请求并解析
  • 执行命令请求
  • 返回命令回复

具体的过程图可以看下边的流程图片: 在这里插入图片描述

处理机制

Redis 服务器是典型的事件驱动系统,Redis将事件分为两大类:

  • 文件事件
  • 时间事件

文件事件

文件事件即Socket读写事件,也就是IO事件,比如客户端连接、命令请求、数据回复、连接断开等等。

Reactor模式详解

Redis 中的Reactor模式实现

Redis 采用单线程的Reactor模式作为其核心事件处理机制,这种设计使其能够在单个线程中高效处理大量并发连接。Reactor模式属于I/O多路复用技术的一种典型实现方式,特别适合高并发、低延迟的场景。

I/O多路复用基础

I/O多路复用(Input/Output Multiplexing)是指使用单个线程管理多个Socket连接的技术。通过系统调用(如select、poll、epoll或kqueue)监视多个文件描述符的状态变化,当某个Socket就绪时,应用程序就能立即得到通知并进行处理,而不需要为每个连接创建独立线程。

Reactor模式核心组件

  1. 事件驱动架构

    • 整个系统围绕事件进行组织
    • 事件包括:连接建立、数据到达、超时等
    • 事件触发相应的处理逻辑
  2. 输入源(Event Sources)

    • 一个或多个并发输入源(如网络Socket)
    • 可以是客户端连接、定时器事件等
    • Redis中主要包括网络I/O事件和时间事件
  3. 服务处理器(ServiceHandler)

    • 也称为Dispatcher或Event Demultiplexer
    • 负责监听和收集所有事件源产生的事件
    • 在Redis中由aeEventLoop实现
  4. 请求处理器(RequestHandlers)

    • 多个具体的事件处理器
    • 每个处理器负责处理特定类型的事件
    • Redis中包括:连接处理器、命令处理器等

工作流程

  1. 事件注册

    • 各种事件源向ServiceHandler注册感兴趣的事件
    • 例如:注册Socket的可读事件
  2. 事件循环

    • ServiceHandler通过I/O多路复用API(如epoll)等待事件
    • 当事件发生时,ServiceHandler被唤醒
  3. 事件分发

    • ServiceHandler同步地将事件分发给对应的RequestHandler
    • 分发过程通常是通过回调机制实现的
  4. 事件处理

    • RequestHandler执行具体的业务逻辑
    • 在Redis中,这包括解析命令、执行命令、返回响应等

Redis中的具体实现

在Redis源码中,Reactor模式的主要实现包括:

  • aeEventLoop:事件循环的核心结构体
  • aeApiPoll:封装底层I/O多路复用API
  • aeCreateFileEvent:注册文件事件
  • aeCreateTimeEvent:注册时间事件

Redis的事件处理流程示例:

  1. 客户端连接到Redis服务器的连接请求到达
  2. aeEventLoop通过epoll检测到新的连接事件
  3. 调用acceptTcpHandler处理新连接
  4. 当客户端发送命令数据时,触发读事件
  5. 调用readQueryFromClient处理命令请求

优势与局限

优势

  • 单线程避免了多线程的上下文切换开销
  • 通过事件驱动实现高并发处理
  • 逻辑简单,避免多线程同步的复杂性

局限

  • 受限于单线程,无法充分利用多核CPU
  • 长时间运行的命令会阻塞整个事件循环
  • 大键操作可能影响响应时间

Redis通过以下方式弥补局限:

  • 对耗时操作(如持久化)使用子进程
  • 6.0+版本支持I/O多线程(但仍保持单线程命令处理)

Reactor 图

下面这些图片可以让你更好地理解 Reactor 模式: 在这里插入图片描述 在这里插入图片描述

多路复用

IO多路复用机制就是通过一种机制,一个进程可以监视多个描述符(Socket),一旦某个描述符就绪,能够通知程序进行相应的复写操作。 IO多路复用机制有这么几种:

  • select
  • poll
  • epoll
  • kqueue

Select

select函数监视的文件描述符分3类:

  • writefds
  • readfds
  • exceptfds

调用后select函数会阻塞,直到有描述符就绪或者超时,函数返回。 当select函数返回后,可以通过 fd 列表遍历,来找到就绪的描述符。

Select优点

几乎在所有平台上都有支持,跨平台支持。

Select缺点

单个进程打开文件描述有一定的限制,有 FD_SETSIZE 设置,默认是 1024 ,采用数组存储,另外在检查数组中是否有文件描述符需要读写时,采用的是线性的扫描(不管是否活跃都扫描轮询),效率较低。

Poll

poll使用一个 pollfd 的指针实现,pollfd结构包含了要监视的 Event 和 发生的 Event,不再使用 select 的参数值传递的方式。

Poll 优点

采用链表的形式存储,它监听的描述符数量没有限制,可以超过select默认限制的1024大小

Poll缺点

另外在检查链表中是否有文件描述符需要读写时,采用线性扫描的方法,即不管Socket是不是活跃的,都轮询一次,效率较低。

epoll

epoll 子啊 Linux2.6 内核中提出的,是之前 select 和 poll 的加强版本。 相对于 select 和 poll 来说,epoll 更加灵活,没有描述符限制。

epoll 使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy就只需要一次。

epoll优点

epoll 没有最大并发连接限制,上限是最大能打开文件的数目,比如1GB内存大约能打开10万文件左右。 epoll 最大的优点就在于只处理活跃的连接,而不需要轮询遍历,所以效率很高。

kqueue

kqueue 是 unix 下的一个 IO 多路复用库。最初是 2000年在FreeBSD系统上开发的一个高性能的事件的事件通知接口。 注册一批Socket描述符kqueue后,当其中描述符状态发生改变时,kqueue将一次性通知应用程序哪些描述符可读可写或出错。

kqueue优点

能处理大量数据,性能较高。