GitHub user zchuango edited 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 的数据面实现。
## Related PRs

- #3428 

## 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]

Reply via email to