在传统的 TCP/IP 网络体系结构的传输层,最典型的两种协议类型是 TCP 和 UDP。RDMA 作为一种高性能网络技术,也有自己独特的传输服务模型。本文将深入介绍 RDMA 传输服务的两个核心维度 ------可靠性 与连接模式,以及由这两个维度组合而成的四种传输服务类型,帮助读者理解如何根据实际场景选择合适的传输服务。
一、TCP 与 UDP 的回顾
在深入了解 RDMA 传输服务之前,先回顾一下传统网络中 TCP 和 UDP 的区别,这对于理解 RDMA 传输服务的设计理念很有帮助。
表 1:TCP 和 UDP 的区别
| 协议 | 连接模式 | 对系统资源的要求 | 正确性 | 数据包顺序 |
|---|---|---|---|---|
| TCP | 基于连接的流模式 | 多 | 保证数据正确性 | 保证数据包顺序 |
| UDP | 无连接的数据报模式 | 少 | 可能丢包 | 不保证 |
TCP 提供 "可靠、有序" 的传输服务,但代价是更高的资源消耗和延迟;UDP 则提供 "轻量、高效" 的传输服务,但不保证数据的可靠性和顺序性。这种权衡在 RDMA 传输服务中同样存在。
二、InfiniBand 传输层概述
RDMA 有自己的传输服务,并在 InfiniBand 体系结构的传输层实现,同时适用于 RoCEv1 和 RoCEv2。
2.1 传输层的核心职责
InfiniBand 传输层负责以下关键功能:
| 功能 | 描述 |
|---|---|
| 有序的数据包交付 | 保证数据包按顺序到达 |
| 分区 | 实现网络隔离和安全 |
| 信道复用 | 多个通信流共享物理链路 |
| 传输服务 | 提供多种可靠性级别的服务 |
| 数据分段与重组 | 发送时按 MTU 分段,接收时重组 |
2.2 数据包的分段与重组机制
发送端的 InfiniBand 传输层根据最大传输单元(MTU)将数据分为适当大小的数据包。接收端基于包含以下信息的基本传输报头重新组装数据包:
- 目的 QPN(Queue Pair Number):标识目标队列对
- 数据包序列号:用于检测丢包和排序
随后接收端确认收到数据包,发送端接收确认消息,并将操作状态更新到 CQ(Completion Queue)。
2.3 硬件实现的优势
InfiniBand 体系结构的传输层相比其他网络体系结构有显著的改进:所有功能都在硬件中实现。这意味着:
- 极低的延迟
- 极高的吞吐量
- 完全的 CPU 卸载
三、传输服务的两个核心维度
在 InfiniBand 协议分层模型中,传输层与 QP(Queue Pair) 强相关。在建立 QP 时,可以选择以下几种传输服务:
- 可靠连接
- 可靠数据报
- 不可靠连接
- 不可靠数据报
- 原始数据报
不考虑原始数据报,从其他传输服务的名称可以看出,InfiniBand 体系结构通过两个维度来描述一种传输服务类型:
┌─────────────────────────────────────────────────────────────┐
│ 传输服务类型维度 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 维度一:可靠性 │
│ ├── 可靠 │
│ └── 不可靠 │
│ │
│ 维度二:连接模式 │
│ ├── 连接 │
│ └── 数据报 │
│ │
└─────────────────────────────────────────────────────────────┘
四、维度一:可靠 / 不可靠
4.1 可靠服务
可靠服务是指通过一些机制保证发送出去的数据包都能够被对方正确无误地接收,属于 "我保证你能正确地收到" 的 "认真负责" 的服务类型。
InfiniBand 标准定义
Reliable Service provides a guarantee that messages are delivered from a requester to a responder at most once, in order and without corruption.
可靠服务保证消息只从发送者向接收者传递一次,并且能够按照顺序完整无损地被接收。
实现可靠服务的关键机制
| 机制 | 功能 | 说明 |
|---|---|---|
| CRC 校验 | 检测受损数据 | 循环冗余校验,在较低协议层进行 |
| 确认机制(ACK) | 确认消息传递 | 使发送者能够确定消息已成功传递 |
| 数据包序列号(PSN) | 检测丢失数据包 | 允许发送者将对端响应与发送请求关联 |
| 计时器 | 检测丢失的确认消息 | 用于检测被丢弃或丢失的 ACK |
可靠服务数据流示意:
发送方 接收方
│ │
│ ──────── 数据包 (PSN=N) ─────────────► │
│ │ CRC校验 ✓
│ │ PSN检查 ✓
│ ◄─────── ACK (确认收到) ────────────── │
│ │
│ 如果计时器超时未收到ACK: │
│ ──────── 重传数据包 ─────────────────► │
注意:CRC 是在较低的协议层进行的,可能会导致数据包在到达传输层之前就被丢弃了。这些丢弃的数据包最终可能在传输层被检测为序列号错误。
4.2 不可靠服务
不可靠服务没有上述机制来保证数据包被正确有序地接收,属于 "我只管发,不管你能不能收到" 的 "不可靠的" 服务类型。
不可靠服务的特点
- ❌ 发送者不会收到消息被接收的确认
- ❌ 不保证数据包的顺序
- ✓ 接收者正常验证传入的数据包(报头字段验证、CRC 检查)
- ✓ 损坏的数据包可能会被默默丢弃,从而导致数据包所属的整个消息被丢弃
- ✓ 在检测到传入数据包中的错误后,接收者不会停止,而是继续接收传入的数据包
- ✓ 接收者认为:一旦以正确的顺序收到完整的消息,并且所有适当的有效性检查都已完成,则接收操作完成
- ✓ 发送者认为:一旦最后或唯一的数据包提交到网络中,消息发送操作就完成了
五、维度二:连接 / 数据报
5.1 面向连接的服务
InfiniBand 标准定义
IBA supports both connection oriented and datagram service. For connected service, each QP is associated with exactly one remote consumer. In this case the QP context is configured with the identity of the remote consumer's queue pair. The remote consumer is identified by a port and a QP number. The port is identified by a local ID (LID) and optionally a Global ID (GID).
翻译:InfiniBand 支持面向连接的服务和数据报服务。对于面向连接的服务,每个 QP 只与一个远端消耗者(指最终会消耗掉数据包的节点上的 QP)关联,即建立连接。在这种情况下,QP Context(QPC) 会使用远端节点的端口和 QPN 进行配置。端口由 LID 和 GID 标识,后者可选。
5.2 连接服务示意
┌─────────────────────────────────────────────────────────────────────────────┐
│ 面向连接的服务示意 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 节点 A 交换机 节点 B │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ LID: │ │ │ │ LID: │ │
│ │ 0x0001 │ │ │ │ 0x0002 │ │
│ │ │ │ │ │ │ │
│ │ QP 0 ◄──┼───────────────────┼─────────┼───────────────────┼──► QP 1 │ │
│ │ │ 连接1 │ │ 连接1 │ │ │
│ │ QP 1 │ │ │ │ QP 0 │ │
│ └─────────┘ │ │ └─────────┘ │
│ │ │ │
│ │ │ ┌─────────┐ │
│ │ │ │ 节点 C │ │
│ │ │ │ LID: │ │
│ │ │ │ 0x0003 │ │
│ ┌─────────┐ │ │ │ │ │
│ │ 节点 A │ │ │ │ QP 0 ◄──┼───┤
│ │ QP 1 ◄──┼───────────────────┼─────────┼───────────────────┼──┐ │ │
│ │ │ 连接2 │ │ 连接2 │ │ │ │
│ └─────────┘ └─────────┘ └──┴──────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
关键特点:
- 每个 QP 只与一个远端 QP 建立连接
- 软件往某个固定 QP 下发的每个 WQE 的目的地都是唯一的
- 如果是 Send 操作,对端网卡会根据下发到对应 QP 的 RQ 中的 WQE 来存放接收到的数据
- 如果是 RDMA Write 操作,对端网卡会直接将数据写入目的缓存
- 如果要更改连接关系,需要修改 QP Context 中的对端 QPN 或 LID(需要两端协商后同时进行)
5.3 数据报服务
对于数据报服务,QP 不与特定的远端 QP 关联,可以向任何兼容的数据报类型 QP 发送消息。
六、四种传输服务类型
把前文介绍的两个传输服务维度组合后,可以得到表 2 中的 4 种传输服务类型。
表 2:四种 RDMA 传输服务类型
| 传输服务维度 | 连接 | 数据报 |
|---|---|---|
| 可靠 | 可靠连接(RC) | 可靠数据报(RD) |
| 不可靠 | 不可靠连接(UC) | 不可靠数据报(UD) |
注意 :其中可靠数据报(RD)在现实中并不存在,下面介绍其他 3 种传输服务的特点。
6.1 可靠连接
┌─────────────────────────────────────────────────────────────┐
│ 可靠连接(RC) │
├─────────────────────────────────────────────────────────────┤
│ ✓ QP 和其他端口的某一个 QP 关联,并且仅和这个 QP 关联 │
│ ✓ 由一个 QP 的发送队列发送的消息会被可靠地传递到另一个 QP │
│ ✓ 数据包按顺序递送 │
│ ✓ 与 TCP 非常相似 │
│ ✓ 支持所有 RDMA 操作类型 │
└─────────────────────────────────────────────────────────────┘
6.2 不可靠连接
┌─────────────────────────────────────────────────────────────┐
│ 不可靠连接(UC) │
├─────────────────────────────────────────────────────────────┤
│ ✓ QP 和其他端口的某一个 QP 关联,并且仅和这个 QP 关联 │
│ ✓ 连接不可靠,因此可能会丢失数据包 │
│ ✓ 传输过程中不会重传有错误的消息 │
│ ✓ 错误处理必须由更高级别的协议提供 │
│ ✓ 支持 Send、Receive、RDMA Write 操作 │
└─────────────────────────────────────────────────────────────┘
6.3 不可靠数据报
┌─────────────────────────────────────────────────────────────┐
│ 不可靠数据报(UD) │
├─────────────────────────────────────────────────────────────┤
│ ✓ QP 可以向任何其他 UD 型 QP 发送消息 │
│ ✓ 可以从任何其他 UD 型 QP 接收消息 │
│ ✓ 发送和接收都不保证成功,数据包可能会被对端丢弃 │
│ ✓ 支持多播消息 │
│ ✓ 与 UDP 非常相似 │
│ ✓ 仅支持 Send、带立即值的 Send、Receive 操作 │
└─────────────────────────────────────────────────────────────┘
七、各传输服务支持的 RDMA 操作
表 3:各种传输服务类型支持的 RDMA 操作
| RDMA 操作 | UD | UC | RC | RD |
|---|---|---|---|---|
| 发送 | √ | √ | √ | |
| 带立即值的发送 | √ | √ | √ | |
| 接收 | √ | √ | √ | |
| RDMA 写 | √ | √ | ||
| 带立即值的 RDMA 写 | √ | √ | ||
| RDMA 读 | √ | |||
| 原子获取和加 | √ | |||
| 原子比较和交换 | √ | |||
| 最大消息大小 | MTU | 1GB | 1GB | 1GB |
关键观察:
- RC 支持所有 RDMA 操作类型,功能最全面
- UD 仅支持发送、带立即值的发送和接收,功能最基础
- UC 支持大部分操作,但不支持 RDMA 读和原子操作
八、资源消耗与可扩展性对比
8.1 问题:既然 RC 功能最全面,是否只用 RC 就够了?
答案是否定的。
凡事有利就有弊。每种传输服务的资源消耗和可扩展性是不同的:
- RC 的资源消耗比 UD 大
- RC 的可扩展性比 UD 弱
可扩展性弱的含义:每个 RDMA 网卡的资源是有限的,如果每个进程消耗过多,能运行的进程自然就少,可扩展性就弱。
8.2 QP 数量对比
假设有 N 个节点 ,每个节点上有 M 个进程,每个进程都要和其他节点上的所有进程通信。
RC 传输服务的 QP 数量
每个节点上使用的 QP 数量 = M² × (N - 1)
┌─────────────────────────────────────────────────────────────────────────────┐
│ RC 传输服务类型需要的 QP 数量示意 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 节点 1 节点 2 节点 3 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐│
│ │ 进程 0 │ │ 进程 0 │ │ 进程 0 ││
│ │ QP 0 ◄──┼───────────────────┼──► QP 0 │ │ ││
│ │ QP 1 ◄──┼───────────────────┼──► QP 1 │ │ ││
│ │ QP 2 │ │ QP 2 ◄──┼───────────────────┼──► QP 0 ││
│ │ QP 3 │ │ QP 3 ◄──┼───────────────────┼──► QP 1 ││
│ │──────────│ │──────────│ │──────────││
│ │ 进程 1 │ │ 进程 1 │ │ 进程 1 ││
│ │ QP 0 ◄──┼───────────────────┼──► QP 0 │ │ ││
│ │ QP 1 ◄──┼───────────────────┼──► QP 1 │ │ ││
│ │ QP 2 │ │ QP 2 ◄──┼───────────────────┼──► QP 0 ││
│ │ QP 3 │ │ QP 3 ◄──┼───────────────────┼──► QP 1 ││
│ └──────────┘ └──────────┘ └──────────┘│
│ │
│ 示例:N=3, M=2 │
│ 每个节点 QP 数量 = 2² × (3-1) = 8 个 QP │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
UD 传输服务的 QP 数量
每个节点上使用的 QP 数量 = M
┌─────────────────────────────────────────────────────────────────────────────┐
│ UD 传输服务类型需要的 QP 数量示意 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 节点 1 节点 2 节点 3 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐│
│ │ 进程 0 │ │ 进程 0 │ │ 进程 0 ││
│ │ QP 0 ◄──┼───────────────────┼──► QP 0 ◄┼───────────────────┼──► QP 0 ││
│ │ │ 任意通信 │ │ 任意通信 │ ││
│ │──────────│ │──────────│ │──────────││
│ │ 进程 1 │ │ 进程 1 │ │ 进程 1 ││
│ │ QP 0 ◄──┼───────────────────┼──► QP 0 ◄┼───────────────────┼──► QP 0 ││
│ │ │ 任意通信 │ │ 任意通信 │ ││
│ └──────────┘ └──────────┘ └──────────┘│
│ │
│ 示例:N=3, M=2 │
│ 每个节点 QP 数量 = 2 个 QP │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
8.3 数量级差异
当 M 和 N 的数值比较大时,两种传输服务类型对资源的消耗会相差几个数量级:
| 场景 | RC 每节点 QP 数 | UD 每节点 QP 数 | 差异 |
|---|---|---|---|
| N=10, M=10 | 10² × 9 = 900 | 10 | 90 倍 |
| N=100, M=100 | 100² × 99 = 990,000 | 100 | 9,900 倍 |
| N=1000, M=100 | 100² × 999 = 9,990,000 | 100 | 99,900 倍 |
九、应用场景选择指南
9.1 RC vs UD 选择建议
┌─────────────────────────────────────────────────────────────────────────────┐
│ 传输服务选择决策树 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 需要可靠传输吗? │
│ │ │
│ ┌────────────┴────────────┐ │
│ │ │ │
│ 是 ▼ 否 ▼ │
│ 需要跨节点大量连接? UD(不可靠数据报) │
│ │ - 资源消耗极低 │
│ │ - 支持多播 │
│ ┌─────────┴─────────┐ - 类似 UDP │
│ │ │ │
│ 是 ▼ 否 ▼ │
│ 考虑资源限制 RC(可靠连接) │
│ 可能需要优化 - 功能最全面 │
│ - 类似 TCP │
│ - 数据完整性和顺序保证 │
│ │
│ 需要连接但不需要可靠? │
│ │ │
│ ▼ │
│ UC(不可靠连接) │
│ - 支持单向 RDMA Write │
│ - 资源消耗介于 RC 和 UD 之间 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
9.2 典型应用场景
| 传输服务 | 典型应用场景 | 原因 |
|---|---|---|
| RC | 分布式存储、数据库复制、MPI 计算 | 数据完整性和可靠性要求高 |
| UD | 服务发现、心跳检测、日志收集、多播消息 | 资源消耗低,可扩展性好 |
| UC | 某些特定的高性能计算场景 | 需要 RDMA Write 但不需要确认 |
9.3 详细对比
| 对比项 | RC | UC | UD |
|---|---|---|---|
| 可靠性 | 高 | 中 | 低 |
| 延迟 | 较高 | 中 | 低 |
| 资源消耗 | 高 | 中 | 低 |
| 可扩展性 | 弱 | 中 | 强 |
| 支持的操作 | 全部 | 部分 | 最少 |
| 类似协议 | TCP | - | UDP |
| 适合场景 | 存储系统 | 特定计算 | 大规模集群 |