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. 设计目标

1. 消除三套实现中的 TCP 握手读写重复代码。
2. 统一 magic、长度、版本、ACK、fallback 和错误结果的处理方式。
3. 支持 client 阻塞式读取和 server 基于 `IOBuf` 的增量读取。
4. 保持 RDMA、URMA、UBSHM 的现有 wire format 完全兼容。
5. 让协议 payload 和高速资源协商逻辑继续由各传输实现负责。
6. 在未开启某一传输宏时,公共组件仍可独立编译。



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

### 当前观点与社区讨论方向

两种方案的核心区别在于公共 Handshake 所处的架构层级。

- **方案 A:Endpoint 级复用 Handshake。**  
  在现有架构基础上,将 RDMA、URMA、UBSHM 中重复的 Handshake 逻辑抽取为公共组件,由各自 Endpoint 
进行复用。该方案主要解决当前握手代码重复的问题,整体改动较小,迁移风险相对较低;但 Handshake 仍然属于各 Endpoint 
建链流程的一部分,Transport 层缺少统一的连接协商、Transport 切换和 fallback 能力。

- **方案 B:将 Handshake 提升到 Transport 级。**  
  默认先建立 TCP 连接,以 TCP 作为统一的控制通道,通过 TCP 交换双方的能力信息以及目标高速通道建链所需的信息;协商成功后,再将连接从 TCP 
升级到 RDMA、URMA、UBSHM 等目标 Transport。Endpoint 则进一步收敛为具体高速数据面的实现,不再承担通用的 TCP 
Handshake、Transport 协商和 fallback 职责。

目前我们更倾向于 **方案 B**。

相比单纯在 Endpoint 层复用 Handshake 代码,方案 B 将“Transport 协商与升级”本身抽象为统一能力。TCP 
可以作为默认的控制面,在 Transport 层统一完成能力协商、建链信息交换、Transport 升级以及失败后的 fallback,而各 Endpoint 
只需要关注对应高速数据面的资源管理和数据传输。

这种方式不仅能够解决当前 RDMA、URMA、UBSHM 之间 Handshake 逻辑重复的问题,也为后续 Transport 能力演进提供了更大的扩展空间。

特别是未来如果需要支持多个 Transport、多个可用数据路径,或者根据双方能力、链路状态等信息进行多路径选择,可以继续在 Transport 
层扩展统一的协商和选择机制,而不需要分别在 RDMA、URMA、UBSHM 等 Endpoint 中重复实现类似逻辑。

因此,我们目前更倾向于方案 B,同时希望听取社区对这一架构方向的意见:

> **公共 Handshake 更适合保留在 Endpoint 层进行复用,还是提升到 Transport 层,形成“默认 TCP 建链 → 通过 TCP 
> 完成能力及建链信息交换 → 升级到目标 Transport”的统一机制,并为后续多 Transport、多路径选择等能力提供扩展基础?**

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