第二部分 · 5 / 7

两种连接方式

第一部分提过:QP 迁移到 RTR 前,必须用带外通道交换对端的 QPN / PSN / GID 等参数。 gordma 给了两条路径——rdma_cm(默认)和 TCP 带外握手, 但产出同样的 Conn

两条路径对比

rdma_cm(默认)推荐
用 librdmacm 的连接管理器。rdmanet.Dial/Listen 接收 "host:port" 地址,协商完成后返回的 QP 已经在 RTS 状态。底层对应 gordma.Dial/ListenCMConn
TCP 带外握手
perftest 的传统做法:自己开个普通 TCP 连接,把 QPN/PSN/GID/LID/RKey/addr 互发,再自己驱动状态机。高层用 WithHandshake() 开启,底层是 handshake 包。

代码上怎么切换

// 默认走 rdma_cm
conn, _ := rdmanet.Dial("10.0.0.1:18515")

// 切换成 TCP 带外握手
conn, _ := rdmanet.Dial("10.0.0.1:18515", rdmanet.WithHandshake())

只差一个 Option,上层 SendMsg/RecvMsg 用法完全一致。

详解:rdma_cm

librdmacm 把 RDMA 的连接建立包装成类似 socket 的流程(rdma_resolve_addrrdma_connect / rdma_accept 等),帮你处理地址解析、路由、参数交换和状态迁移。 底层返回的是 CMConn,可取出已就绪的 QP/CQ/PD:

cmConn.QP()   // *QP,已在 RTS
cmConn.CQ()   // *CQ
cmConn.PD()   // *PD

优点:地址用熟悉的 IP:port,路由、RNR 重试等由 CM 处理;这是大多数场景的首选。

详解:TCP 握手

handshake 包是纯 Go(无 cgo),所以能在任何平台单测。两端交换的就是 EndpointInfo(节选自 handshake.go,以换行分隔的 JSON 帧互发):

type EndpointInfo struct {
    QPN        uint32   // 队列对号
    PSN        uint32   // 起始包序列号
    LID        uint16   // InfiniBand LID(RoCE 下为 0)
    GID        [16]byte // RoCE/IB 全局标识
    GIDIndex   int      // 对端用的 GID 索引
    RKey       uint32   // RDMA Write/Read 的远端 key
    RemoteAddr uint64   // 对端已注册缓冲区的虚拟地址
}

拿到对端的 EndpointInfo 后,自己填好 RCConnParamsModifyToInit/RTR/RTS。这正是 perftest 默认采用的方式,工具里用 -p 指定 TCP 握手端口。

怎么选

场景建议
一般业务、想省事默认 rdma_cm
对标 perftest / 需要完全掌控握手内容TCP 握手WithHandshake / -p
环境里 rdma_cm 不可用或受限TCP 握手
下一步 连接建好后,还能用一组 Option 调性能。下一节讲 性能选项
gordma 教程