GitHub user zchuango created a discussion: RDMA、URMA 与 UBSHM 公共握手组件设计方案
## 1. 背景
brpc 当前有三种基于 TCP 建立控制连接、再切换到高速数据面的传输:
- RDMA:`src/brpc/rdma/rdma_endpoint.*`
- URMA:`src/brpc/urma/urma_endpoint.*`
- UBSHM/UBRING:`src/brpc/ubshm/ub_endpoint.*`
三套实现都包含以下流程:
```text
TCP 连接建立
-> 发送本地 Hello
-> 接收并校验对端 Hello
-> 创建或导入高速传输资源
-> 返回协商结果/ACK
-> 成功切换高速通道,或回退到 TCP
```
目前公共流程主要以复制代码的形式存在。例如:
- RDMA 的 TCP 握手读写循环在 `rdma_endpoint.cpp` 中实现;
- URMA 在 `urma_endpoint.cpp` 中重新实现了一套;
- UBSHM 在 `ub_endpoint.cpp` 中再次实现了一套;
- 三套实现都维护独立的握手状态枚举、client/server 流程和 fallback 逻辑。
本方案只抽取握手公共组件,不尝试合并 RDMA QP、URMA Jetty 或 UBSHM ring 的数据面实现。
## 2. 现状分析
### 2.1 RDMA
RDMA 支持:
- v2 二进制握手:`RDMA` magic;
- v3 protobuf 握手:`RDM3` magic;
- client/server 两套解析路径;
- server 侧基于 `butil::IOBuf` 的增量解析;
- 非 RDMA 连接的 TCP fallback;
- 4 字节 ACK。
相关代码:
- `src/brpc/rdma/rdma_handshake.h`
- `src/brpc/rdma/rdma_handshake.cpp`
- `src/brpc/rdma/rdma_handshake_server.cpp`
### 2.2 URMA
URMA 的握手结构与 RDMA 类似,但 payload 完全不同:
- v2 二进制握手:`URMA` magic;
- v3 protobuf 握手:`URM3` magic;
- Hello 中携带 EID、UASID、Jetty、segment 和 token 信息;
- 需要先导入 remote segment,再导入 remote Jetty;
- 目前 server/client 主要由 `UrmaEndpoint` 自己驱动。
相关代码:
- `src/brpc/urma/urma_handshake.h`
- `src/brpc/urma/urma_handshake.cpp`
- `src/brpc/urma/urma_endpoint.cpp`
### 2.3 UBSHM/UBRING
UBSHM 当前使用固定长度二进制握手:
```text
[ "UB" 2B ][ HelloMessage 62B ]
```
Hello 中携带:
- `msg_len`
- `hello_ver`
- `impl_ver`
- shared memory 长度
- shared memory 名称
握手成功后,通过 ACK 中的 `UB_OK` bit 表示是否切换到 UBRING;失败时回退 TCP。
相关代码:
- `src/brpc/ubshm/ub_endpoint.h`
- `src/brpc/ubshm/ub_endpoint.cpp:63`
- `src/brpc/ubshm/ub_endpoint.cpp:331`
- `src/brpc/ubshm/ub_endpoint.cpp:460`
## 3. 设计目标
### 3.1 目标
1. 消除三套实现中的 TCP 握手读写重复代码。
2. 统一 magic、长度、版本、ACK、fallback 和错误结果的处理方式。
3. 支持 client 阻塞式读取和 server 基于 `IOBuf` 的增量读取。
4. 保持 RDMA、URMA、UBSHM 的现有 wire format 完全兼容。
5. 让协议 payload 和高速资源协商逻辑继续由各传输实现负责。
6. 在未开启某一传输宏时,公共组件仍可独立编译。
### 3.2 非目标
本次不抽取以下内容:
- RDMA QP/CQ/PD/MR 创建和销毁;
- URMA Jetty/JFR/JFC/segment 导入;
- UBSHM ring 和 shared memory 映射;
- RDMA、URMA、UBSHM 的数据面 completion 处理;
- 三种传输的内存池;
- 三种传输的 poller/CQ 事件线程。
这些组件虽然也存在结构相似性,但资源模型不同,过早抽象会导致公共接口暴露 `ibv_*`、`urma_*` 或 UBRING 类型。
## 4. 总体架构
新增公共目录:
```text
src/brpc/handshake/
handshake_common.h # 结果、角色、错误和阶段定义
handshake_io.h # 字节流抽象
handshake_io.cpp
handshake_frame.h # magic/长度/增量帧解析工具
handshake_frame.cpp
handshake_driver.h # 通用握手驱动
handshake_driver.cpp
```
总体关系如下:
```text
+--------------------------+
| HandshakeDriver |
| client/server state flow |
+------------+-------------+
|
+------------v-------------+
| HandshakeProtocol |
| build/parse/validate hello|
| build/parse ack |
+--+-------------+----------+
| |
+---------v--+ +----v---------+
| HandshakeIO| | FrameCodec |
| read/write | | magic/length |
+---------+--+ +--------------+
|
+------------+-------------+-------------+
| | |
RDMA adapter URMA adapter UBSHM adapter
```
公共组件只负责握手过程和字节帧,不负责判断“创建 QP、导入 Jetty 还是映射 SHM”。
## 4.1 两种候选架构对比
本节同时保留两种方案,便于社区讨论握手组件应该放在哪一层。
### 方案 A:公共握手组件与各类 Endpoint 组合
这是上一版设计的方向:公共组件提供 `HandshakeIO`、`FrameCodec` 和部分握手驱动,各类 Transport/Endpoint
负责组合调用。TCP fallback 仍由具体 Transport 维护,Endpoint 仍可能参与握手生命周期。
```mermaid
flowchart TB
Socket["Socket"] --> T["RdmaTransport / UrmaTransport / UBShmTransport"]
T --> TCP["TcpTransport\nTCP fallback"]
T --> HS["HandshakeSession\n公共握手驱动"]
HS --> IO["HandshakeIO / FrameCodec"]
HS --> EP["RdmaEndpoint / UrmaEndpoint / UBShmEndpoint"]
EP --> EHS["Endpoint handshake callbacks\nBuildHello / ParseHello /
Negotiate"]
EP --> DP["高速数据面\nQP / Jetty / UBRing"]
T --> FB["Transport fallback state"]
FB --> TCP
```
该方案可以先复用 frame 和 I/O 代码,改动较小;但握手生命周期仍然横跨 Transport 和 Endpoint,Endpoint 与 TCP
控制连接之间仍存在一定耦合。
### 方案 B:Handshake 放到 Transport 上层,Endpoint 只负责数据面
这是本次建议的方向:默认先建立 TCP 连接,客户端可以请求升级到 RDMA、URMA 或 UBSHM。握手、升级决策、fallback 和 active
transport 选择全部由上层 Transport 负责。
```mermaid
flowchart TB
Socket["Socket"] --> UT["UpgradeTransport / NegotiatingTransport"]
UT --> TCP["TcpTransport\n默认控制面与 TCP 数据面"]
UT --> HS["HandshakeSession\n连接协商 / 版本 / ACK / fallback"]
HS --> IO["HandshakeIO + FrameCodec"]
UT --> PF["UpgradeProvider\n选择协议与 Endpoint factory"]
PF --> R["RdmaEndpoint\n仅高速数据面"]
PF --> U["UrmaEndpoint\n仅高速数据面"]
PF --> B["UBShmEndpoint\n仅高速数据面"]
R --> RD["QP / CQ / MR"]
U --> UD["Jetty / JFC / Segment"]
B --> BD["UBRing / Shared Memory"]
UT --> ST["TransportState\nTCP_ACTIVE / UPGRADING / HIGH_SPEED_ACTIVE"]
ST --> TCP
ST --> R
ST --> U
ST --> B
```
方案 B 中,Endpoint 不再执行 TCP fd 读写、magic 判断、握手 bthread 或 TCP fallback。它只向 Transport
提供资源准备、Hello payload、远端参数应用和数据面操作。
### 两种方案的主要差异
```mermaid
flowchart LR
A["方案 A\nHandshake 与 Endpoint 组合"] --> A1["公共代码复用较快"]
A --> A2["Endpoint 仍参与连接控制"]
A --> A3["Fallback 状态分散"]
A --> A4["后续新增传输仍需接入 Endpoint 握手"]
B["方案 B\nHandshake 位于 Transport"] --> B1["TCP 是统一默认控制面"]
B --> B2["Endpoint 只负责高速数据面"]
B --> B3["Fallback 与 active path 统一"]
B --> B4["新增传输只需提供 Provider"]
```
## 4.2 方案 B 的连接升级时序
### 客户端请求高速传输
```mermaid
sequenceDiagram
participant C as Client
participant T as UpgradeTransport
participant TCP as TcpTransport
participant H as HandshakeSession
participant E as High-speed Endpoint
participant S as Server Transport
C->>T: Connect()
T->>TCP: Establish TCP connection
TCP-->>T: TCP connected
T->>H: StartClient()
H->>TCP: Send high-speed Hello
TCP->>S: Hello over TCP
S->>S: Select RDMA / URMA / UBSHM provider
S-->>TCP: Remote Hello
TCP-->>H: Receive Remote Hello
H->>E: Parse remote parameters / prepare resources
E-->>H: Resource negotiation result
alt Upgrade succeeds
H->>TCP: Send enabled ACK
H->>T: NEGOTIATED
T->>E: Activate()
T-->>C: Connect done, high-speed active
else Upgrade unavailable or resource failure
H->>TCP: Send disabled ACK
H->>T: FALLBACK
T-->>C: Connect done, TCP active
end
```
### 服务端识别客户端请求
```mermaid
sequenceDiagram
participant TCP as TcpTransport
participant T as UpgradeTransport
participant H as HandshakeSession
participant E as Selected Endpoint
participant IM as InputMessenger
TCP->>T: TCP readable event
T->>T: Peek magic
alt Magic matches upgrade protocol
T->>H: Create protocol handshake
H->>T: Consume Hello frame
H->>E: Parse and negotiate resources
alt Negotiation succeeds
E-->>H: Ready
H->>T: Activate endpoint
T->>E: Future data events
else Negotiation fails
H->>T: Fallback to TCP
T->>IM: Continue normal TCP parsing
end
else Magic does not match
T->>T: Push back inspected bytes
T->>IM: Continue normal TCP parsing
end
```
GitHub link: https://github.com/apache/brpc/discussions/3432
----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: [email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]