第二部分 · 5 / 7
两种连接方式
第一部分提过:QP 迁移到 RTR 前,必须用带外通道交换对端的 QPN / PSN / GID 等参数。
gordma 给了两条路径——rdma_cm(默认)和 TCP 带外握手,
但产出同样的 Conn。
两条路径对比
rdma_cm(默认)推荐
用 librdmacm 的连接管理器。
rdmanet.Dial/Listen 接收 "host:port" 地址,协商完成后返回的 QP 已经在 RTS 状态。底层对应 gordma.Dial/Listen 与 CMConn。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_addr →
rdma_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 后,自己填好 RCConnParams 调
ModifyToInit/RTR/RTS。这正是 perftest 默认采用的方式,工具里用 -p 指定 TCP 握手端口。
怎么选
| 场景 | 建议 |
|---|---|
| 一般业务、想省事 | 默认 rdma_cm |
| 对标 perftest / 需要完全掌控握手内容 | TCP 握手(WithHandshake / -p) |
| 环境里 rdma_cm 不可用或受限 | TCP 握手 |
下一步
连接建好后,还能用一组 Option 调性能。下一节讲 性能选项。
gordma 教程