MediaDriver 实现分析

1. 概述

MediaDriver 是 Aeron 的核心运行时,负责管理所有网络 I/O、流控、日志缓冲区分配和客户端管理。它运行在独立进程/线程中,与客户端应用通过共享内存 IPC 通信。

MediaDriver 的核心设计是三线程模型

┌─────────────────────────────────────────────────────────┐
│                    MediaDriver Context                   │
│  配置所有组件:线程工厂、Idle策略、流控策略、日志工厂等      │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ Conductor    │  │  Sender      │  │  Receiver    │  │
│  │ (主协调线程)  │  │  (发送线程)   │  │  (接收线程)   │  │
│  │              │  │              │  │              │  │
│  │ 命令处理      │  │ UDP Send     │  │ UDP Recv     │  │
│  │ 资源管理      │  │ NAK 处理     │  │ 数据分发      │  │
│  │ 超时检查      │  │ 流控检查      │  │ Status Msg   │  │
│  │ 命名解析      │  │ 拥塞控制      │  │ NAK 发送     │  │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  │
│         │                 │                 │          │
│         │  ┌──────────────┼─────────────────┘          │
│         │  │              │                            │
│    ┌────▼──▼──────────┐   │                            │
│    │  Shared Memory   │◄──┘                            │
│    │  (Ring Buffers,  │                                │
│    │   Counters,      │                                │
│    │   Log Buffers)   │                                │
│    └──────────────────┘                                │
└─────────────────────────────────────────────────────────┘
  • Conductor:处理所有管理操作(创建/销毁 Publication、Subscription),周期性超时检查
  • Sender:轮询所有 NetworkPublication,发送数据到网络,处理 NAK 和流控
  • Receiver:接收 UDP 数据,重建 Image,发送 Status Message 和 NAK

2. MediaDriver 入口

MediaDriver.java 是驱动的主入口:

MediaDriver.launch()
  ├── Context.conclude()              // 完成配置,创建所有组件
  │   ├── 创建 LogFactory             // 日志缓冲区工厂
  │   ├── 创建 CountersManager        // 指标计数器
  │   ├── 创建 Receiver → ReceiverProxy
  │   ├── 创建 Sender → SenderProxy
  │   ├── 创建 DriverConductor        // 核心协调器
  │   └── 创建 Command Queues         // IPC 环形缓冲区
  ├── AgentRunner.startOnThread()     // 启动线程
  │   ├── Conductor Agent 线程
  │   ├── Sender Agent 线程
  │   └── Receiver Agent 线程
  └── 等待 ShutdownSignalBarrier      // 阻塞直到收到关闭信号

每个线程都是 Agent 接口的实现,由 AgentRunner 驱动事件循环:

// Agent 核心接口
public interface Agent {
    int doWork() throws Exception;    // 有工作可做时返回 >0
    String roleName();                 // 线程名称
    void onClose();                    // 关闭回调
}

AgentRunner 持续调用 doWork(),返回 0 时使用 IdleStrategy 进行退避(spin/yield/sleep)。

驱动启动关键代码

MediaDriver.java:4842Context.conclude() 方法 ~600 行,构建所有组件:

Context.conclude()
  ├── 验证配置参数
  ├── 创建 SystemCounters               // 系统计数器
  ├── 创建 CountersManager              // 计数器管理器
  ├── 创建 LossReport                   // 丢包报告
  ├── 创建 Ring Buffer:
  │   ├── toDriverCommands              // Client → Driver 命令
  │   ├── toClientsBroadcast            // Driver → Client 事件
  │   └── countersValuesBuffer          // 计数器值
  ├── 创建 Receiver                     // 接收 Agent
  ├── 创建 Sender                       // 发送 Agent
  ├── 创建 DriverConductor              // 协调 Agent
  └── 设置 ShutdownSignalBarrier        // 关闭信号

3. DriverConductor:核心协调器

DriverConductor.java (3549 行) 是 Aeron 驱动的心脏。它实现 Agent 接口,在独立线程中运行事件循环。

3.1 管理的资源

DriverConductor.java:204-211

networkPublications      // 网络发布者列表
ipcPublications          // IPC 发布者列表
publicationImages        // 接收到的发布镜像
publicationLinks         // 客户端创建的 Publication 连接
subscriptionLinks        // 客户端创建的 Subscription 连接
counterLinks             // 客户端创建的 Counter 连接
clients                  // 已连接的客户端列表
sendChannelEndpointByChannelMap    // UDP 发送通道
receiveChannelEndpointByChannelMap // UDP 接收通道

3.2 事件循环 (doWork)

Conductor 的 doWork() 主循环:

doWork()
  ├── 1. toDriverCommands.read()       // 读取客户端命令
  │   ├── ADD_PUBLICATION              // 创建 Publication
  │   │   ├── 分配 sessionId
  │   │   ├── 创建 Log Buffer (3 term)
  │   │   ├── 创建 NetworkPublication / IpcPublication
  │   │   └── 通过 senderProxy 通知 Sender
  │   ├── REMOVE_PUBLICATION           // 移除 Publication
  │   ├── ADD_SUBSCRIPTION             // 添加订阅
  │   │   ├── 创建 SubscriptionLink
  │   │   ├── 绑定 ReceiveChannelEndpoint
  │   │   └── 通过 receiverProxy 通知 Receiver
  │   └── REMOVE_SUBSCRIPTION          // 移除订阅
  ├── 2. driverCmdQueue.drain()        // 内部命令队列
  │   ├── 定时器到期 → 超时检查
  │   ├── reResolution → DNS 重新解析
  │   └── 其他异步任务
  ├── 3. 超时检查(周期性)
  │   ├── 客户端心跳超时 → 清理
  │   ├── Publication 空闲超时
  │   └── 命名解析缓存过期
  └── 4. endOfLifeResources 清理       // 回收已关闭的资源

3.3 Proxy 模式

Conductor 与 Sender/Receiver 的通信通过 Proxy 模式实现——将操作序列化为命令消息,通过队列发送:

DriverConductor
      │
      ├── senderProxy.commandQueue      // OneToOneConcurrentArrayQueue
      │   └── Sender 在 doWork() 中 consume
      │
      └── receiverProxy.commandQueue    // OneToOneConcurrentArrayQueue
          └── Receiver 在 doWork() 中 consume

这种设计确保 Conductor、Sender、Receiver 线程无锁共享状态,每条命令只被一个线程消费。


4. Sender:发送线程

Sender.java (232 行) 实现发送端逻辑:

Sender.doWork()
  ├── 1. commandQueue.drain()           // 处理 Conductor 的命令
  │   ├── newNetworkPublication         // 注册新网络发布
  │   ├── removeNetworkPublication      // 移除网络发布
  │   └── 其他管理命令
  ├── 2. 轮询所有 NetworkPublication
  │   ├── send()                        // 从 term buffer 读数据发送
  │   │   ├── 检查 term buffer tail counter
  │   │   ├── 组装 DataHeader 帧
  │   │   ├── UDP send()
  │   │   └── 更新 sender position
  │   ├── onStatusMessage()             // 处理 Status Message
  │   │   ├── 更新 receiver position
  │   │   ├── 检查 NAK → 触发重传
  │   │   └── 更新流控窗口
  │   └── onNakMessage()                // 处理 NAK 消息
  │       └── 从 term buffer 找到对应数据 → 重传
  ├── 3. 发送 NAK 重传
  │   └── RetransmitHandler 处理积压的 NAK
  └── 4. 心跳(周期性)
      └── 发送 Setup 帧(新订阅者同步)

4.1 Padding 消除伪共享

Sender.java:32-54 通过继承层次添加 padding 字段来防止 CPU cache line 的伪共享:

class SenderLhsPadding { byte p000...p063; }        // 64 bytes padding
class SenderHotFields extends SenderLhsPadding { ... } // 热字段
class SenderRhsPadding extends SenderHotFields { ... } // 64 bytes padding

5. Receiver:接收线程

Receiver.java (365 行) 实现接收端逻辑:

Receiver.doWork()
  ├── 1. commandQueue.drain()           // 处理 Conductor 的命令
  │   ├── addSubscription               // 添加订阅(绑定 endpoint)
  │   ├── removeSubscription            // 移除订阅
  │   └── addDestination / removeDestination
  ├── 2. dataTransportPoller.poll()     // 轮询所有 ReceiveChannelEndpoint
  │   └── 对每个 endpoint:
  │       └── UDP recv() → onDataPacket()
  │           ├── 解析 DataHeader (streamId, sessionId, termId, termOffset)
  │           ├── 查找/创建 PublicationImage
  │           ├── 写入 Image 的 term buffer(重建)
  │           ├── 检测 gap → 触发 NAK
  │           └── 更新 subscriber position
  ├── 3. 轮询所有 PublicationImage
  │   └── sendStatusMessage()           // 发送 Status Message
  │       ├── 报告 receiver position (消费进度)
  │       ├── 报告 receiver HWM (窗口上限)
  │       └── 附带 NAK (如有 gap)
  └── 4. 清理超时的 PendingSetup

6. IPC 通信架构

Driver 与 Client 之间通过 CNC (Command and Control) 共享内存文件通信:

┌────────────────────────────────────────────────┐
│              CNC 共享内存文件                     │
│  ┌──────────────────────────────────────────┐  │
│  │  toDriver Ring Buffer (ManyToOne)        │  │  ← Client → Driver 命令
│  │  ADD_PUBLICATION / REMOVE_PUBLICATION     │  │
│  │  ADD_SUBSCRIPTION / REMOVE_SUBSCRIPTION   │  │
│  ├──────────────────────────────────────────┤  │
│  │  toClients Broadcast Buffer              │  │  ← Driver → Client 事件
│  │  ON_PUBLICATION_READY / ON_SUBSCRIPTION   │  │
│  │  ON_AVAILABLE_IMAGE / ON_UNAVAILABLE_IMAGE│  │
│  ├──────────────────────────────────────────┤  │
│  │  Counters Metadata Buffer                │  │  ← 计数器元数据
│  ├──────────────────────────────────────────┤  │
│  │  Counters Values Buffer                  │  │  ← 计数器值(原子更新)
│  ├──────────────────────────────────────────┤  │
│  │  Error Log Buffer                        │  │  ← 错误日志
│  └──────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────┐  │
│  │  Log Buffers (每个 Publication/Image)     │  │
│  │  ┌──────┬──────┬──────┬──────────┐       │  │
│  │  │Term0 │Term1 │Term2 │ MetaData │       │  │
│  │  └──────┴──────┴──────┴──────────┘       │  │
│  └──────────────────────────────────────────┘  │
└────────────────────────────────────────────────┘

详见 Client API 与 IPC


7. 关键设计决策

决策 实现 说明
三线程模型 Conductor/Sender/Receiver 管理、发送、接收职责分离
Agent 事件循环 doWork() + AgentRunner 统一线程模型,IdleStrategy 可配置
Proxy 模式解耦 Queue-based 命令传递 Conductor 不直接调用 Sender/Receiver
Cache Line Padding LhsPadding / RhsPadding 消除伪共享,提升 Sender 吞吐
共享内存 IPC Memory-Mapped Files Driver 与 Client 零拷贝通信
定时器驱动 timerCheckDeadlineNs 周期性超时检查,避免额外定时器线程
Shutdown 信号 ShutdownSignalBarrier 优雅关闭,等待各线程完成当前工作

8. 关键源文件索引

文件 行数 说明
MediaDriver.java 4842 驱动入口 + 配置上下文
DriverConductor.java 3549 核心协调 Agent
Sender.java 232 发送 Agent
Receiver.java 365 接收 Agent
DriverConductorProxy.java ~300 Sender/Receiver → Conductor 代理
ClientProxy.java ~150 Driver → Client 事件广播代理
SenderProxy.java ~200 Conductor → Sender 命令代理
ReceiverProxy.java ~200 Conductor → Receiver 命令代理

本站访问量

This site uses Just the Docs, a documentation theme for Jekyll.