Aeron 架构概述
1. Aeron 是什么
Aeron 是一个高效率、低延迟的可靠消息传输系统。它基于 UDP 单播/多播和共享内存 IPC,在应用层实现可靠性保证,核心设计目标是最高的吞吐量和最低、最可预测的延迟。
传统 TCP 消息中间件的根本问题:
TCP: [消息1]→[消息2]→[消息3丢失]→[消息2重传]→...→[消息3]→ ← 队头阻塞
必须等消息2重传成功才能发消息3,即便消息3和消息2无关
Aeron: [msg1]→[msg2]→[msg3]→[msg2 NAk]→[msg2重传]→[msg4]→ ← 无队头阻塞
消息3不需要等待消息2完成,接收端后补丢失的消息2
Aeron 通过 Term Buffer 设计、NAK 协议和 Lock-Free 架构 解决这些根本问题。
2. 核心创新
2.1 Term Buffer:三区段日志缓冲区
Aeron 的核心数据结构是一个划分为 3 个 term 的 log buffer:
┌──────────────────────────────┐
│ Term 0 │ ← 当前写入区(active)
├──────────────────────────────┤
│ Term 1 │ ← 已写完,等待清理
├──────────────────────────────┤
│ Term 2 │ ← 最旧的数据
├──────────────────────────────┤
│ Log Meta Data │ ← 全局元数据(tail counter、term id、状态等)
└──────────────────────────────┘
- 循环使用:Publisher 写到 Term 0 末尾后翻转到 Term 1(此时 Term 1 成为 active),Term 0 等待清理后复用
- 零拷贝:数据直接写入内存映射文件,reader 直接读同一块内存
- Lock-Free:通过原子 CAS 操作推进 tail counter,避免锁竞争
2.2 NAK 可靠性协议
不同于 TCP 的 ACK(确认每个包),Aeron 使用 NAK(Negative Acknowledgment):
Sender Receiver
│ │
│──Data(seq=1)──────────────────────→│ ✓ 收到
│──Data(seq=2)──────────────────────→│ ✓ 收到
│──Data(seq=3)──X──(丢失) │
│──Data(seq=4)──────────────────────→│ ✓ 收到
│ │
│←─────Status(1,2,4)+NAK(3)─────────│ "我收到了1,2,4,请重发3"
│ │
│──Data(seq=3)重传──────────────────→│ ✓ 补齐
- 接收端只报告”丢了什么”,不确认”收到了什么”
- 发送端不等待 ACK,持续发送,只在收到 NAK 时重传
- 避免了 TCP 的滑动窗口队头阻塞
2.3 Lock-Free 设计哲学
所有热路径数据结构避免锁:
- Term Buffer tail counter:CAS 原子递增,多 producer 线程安全
- Command Queue:Ring Buffer 实现,单生产者/单消费者或一对多模式
- Counters:基于
AtomicLong/UnsafeBuffer的原子计数器
3. 整体架构
Aeron 采用分层架构,核心分为 Java 实现(主代码库)和 C 实现(独立但等效的客户端与驱动):
┌──────────────────────────────────────────────────────────┐
│ 应用层 │
│ ├─ Publication (发送消息) │
│ ├─ Subscription (接收消息) │
│ └─ Cluster Client / Archive Client │
├──────────────────────────────────────────────────────────┤
│ Client API 层 (aeron-client/) │
│ ├─ Aeron: 入口门面,管理 Publication/Subscription │
│ ├─ ClientConductor: 客户端核心协调器 │
│ ├─ Publication / ExclusivePublication: 消息发送 │
│ ├─ Subscription / Image: 消息接收 │
│ └─ DriverProxy: 与 MediaDriver 的进程间通信 │
├──────────────────────────────────────────────────────────┤
│ IPC 通信层 (Shared Memory / Ring Buffer) │
│ ├─ Command Ring Buffer (to-driver / to-clients) │
│ ├─ Broadcast Buffer (driver events → clients) │
│ ├─ Counters Buffer (指标) │
│ └─ Log Buffers (消息数据) │
├──────────────────────────────────────────────────────────┤
│ Media Driver 层 (aeron-driver/) │
│ ├─ DriverConductor: 管理所有 Publication/Subscription │
│ ├─ Sender: UDP 发送线程 + NAK 处理 │
│ ├─ Receiver: UDP 接收线程 + 数据分发 │
│ ├─ Flow Control: Min/Max/Multicast 流控 │
│ └─ Congestion Control: 拥塞检测与窗口调整 │
├──────────────────────────────────────────────────────────┤
│ 网络传输层 (UDP / IPC) │
│ ├─ UDP Unicast: 单播消息 + MDC (Multi-Destination-Cast) │
│ ├─ UDP Multicast: 原生多播 │
│ └─ IPC (Shared Memory): 同机进程间直接内存映射 │
├──────────────────────────────────────────────────────────┤
│ Archive 层 (aeron-archive/) │
│ ├─ Recording: 流持久化到磁盘 │
│ ├─ Replay: 从磁盘回放 │
│ └─ Catalog: 索引和元数据管理 │
├──────────────────────────────────────────────────────────┤
│ Cluster 层 (aeron-cluster/) │
│ ├─ ConsensusModule: Raft 共识引擎 │
│ ├─ Election: Leader 选举 │
│ ├─ LogReplication: 日志复制 │
│ └─ ClusterServiceExtension: 有状态服务接口 │
└──────────────────────────────────────────────────────────┘
4. 一次消息发送的完整生命周期
结合 初始化流程 和 Client API 的分析,一次消息从发送到接收的完整路径:
┌──────────┐ ┌──────────────┐ ┌─────────┐ ┌──────────┐ ┌───────────┐
│ 启动阶段 │───→│ 消息发送 │───→│ UDP传输 │───→│ 消息接收 │───→│ 可靠性处理 │
│ (一次) │ │ (每条消息) │ │ (每帧) │ │ (每帧) │ │ (按需) │
└──────────┘ └──────────────┘ └─────────┘ └──────────┘ └───────────┘
启动阶段(进程启动时执行一次):
1. MediaDriver 启动:创建 Conductor/Sender/Receiver 线程
2. 创建共享内存区域(CNC: Command and Control)
3. Client 通过 Ring Buffer 发送 ADD_PUBLICATION 命令
4. Driver 创建 Log Buffer(3 个 term + metadata)
5. Driver 返回 publication 参数给 Client
消息发送:
1. Publication.offer(buffer) -> 写入 term buffer 的 active term
2. CAS 推进 tail counter
3. Sender 线程扫描 term buffer 中的脏数据
4. Sender 组装 UDP 数据帧并发送
5. 重复直到消息发送完毕
网络传输:
1. UDP 帧通过 OS 网络栈发送
2. 多播场景: 单次 send 到多播组
3. MDC(单播多目标): 分别发送到每个目标地址
消息接收:
1. Receiver 线程 poll UDP socket
2. 解析 DataHeader,提取 streamId/sessionId/position
3. 将数据写入本地 Image 的 term buffer(重建)
4. Subscriber 的 poll 回调中读取 term buffer 中的消息
可靠性处理:
1. Receiver 检测到 gap(seq 跳跃)
2. Receiver 发送 Status Message + NAK
3. Sender 收到 NAK,从 term buffer 找到对应数据重传
4. 数据流持续,不阻塞
5. 关键设计决策
| 决策 | 说明 | 代价 |
|---|---|---|
| UDP + 应用层可靠 | 避免 TCP 队头阻塞,可控的重传策略 | 需要自己实现可靠性 |
| 3 个 term 循环 | 最小化内存占用,term 写完即翻转 | term 必须够大以容纳飞行中的数据 |
| NAK 而非 ACK | 减少反向流量,只报告丢失 | 丢包检测有延迟(等后续包到达) |
| Lock-Free CAS | 消除上下文切换,极低延迟 | CPU 自旋消耗,单线程吞吐有限 |
| Flyweight 协议帧 | 直接在 UnsafeBuffer 上读字段,零反序列化 | 字段顺序和大小固定,扩展性受限 |
| Java + C 双实现 | Java 易于开发,C 避免 GC 停顿 | 两套代码需保持协议兼容 |
| 共享内存 IPC | 同机进程间延迟极低(~100ns) | 仅限同机 |
6. 各子系统文档入口
| 子系统 | 文档 | 核心类 |
|---|---|---|
| 代码结构 | 02-代码结构分析 | module 布局、Gradle 构建 |
| MediaDriver | 03-MediaDriver分析 | MediaDriver, DriverConductor, Sender, Receiver |
| LogBuffer 与协议 | 04-LogBuffer与协议 | LogBufferDescriptor, DataHeaderFlyweight |
| Client API & IPC | 05-ClientAPI与IPC | Aeron, Publication, Subscription, ClientConductor |
| 流控与拥塞控制 | 06-流控与拥塞控制 | FlowControl, CongestionControl, LossDetector |
| Archive | 07-Archive分析 | ArchiveConductor, Catalog, RecordingSession, ReplaySession |
| Cluster | 08-Cluster分析 | ConsensusModuleAgent, Election, RecordingLog, SessionManager |
| C/C++ 实现 | 09-C与C++实现 | aeronc.h, aeronmd.h, aeron_driver_conductor.c |
| 性能优化技巧 | 10-性能优化技巧 | Lock-Free CAS、堆外内存、VarHandle、缓存行对齐、sendmmsg、热路径无分配 |