UrmaTransport 是使用 openEuler URMA(Unified Remote Memory Access)SDK 实现的传输层。它是 #3217 中提出的路线 B, 与基于 OBMM 的 UBRing 传输(路线 A, #3226)互补,承担大包/跨节点 高吞吐场景,共同构成路线 C(双后端)。
URMA 在 UMDK 支持的设备上提供 verbs 风格接口。当前实现创建可靠消息 (URMA_TM_RM)Jetty,并使用 CTP 传输路径;通过 urma_post_jetty_send_wr 提交发送 WR,通过 urma_post_jfr_wr 提交接收 WR。完成事件既可由 JFC 忙轮询获取,也可通过 JFCE 事件 fd 获取。
# 带 URMA 支持编译 brpc cmake -B build -DWITH_URMA=ON make -C build -j$(nproc) # 编译 urma_performance 示例 cd example/urma_performance cmake -B build make -C build -j$(nproc)
WITH_URMA=ON 使用上游 UMDK 头文件进行编译。CMake 优先使用系统安装的 SDK;找不到头文件时,会参照 Mooncake 的 mock 构建方式下载固定版本的 UMDK commit,可通过 DOWNLOAD_URMA_HEADERS=OFF 禁止下载。找到 liburma 时使用 真实硬件数据通路,否则链接 brpc 的 mock,使 URMA 代码和测试仍可在无硬件 环境编译。URMA_USE_MOCK 默认为 AUTO;设为 ON 可强制使用 mock,设为 OFF 则要求找到 liburma 并排除 mock。
sh config_brpc.sh --headers=/usr/include --libs=/usr/lib \ --with-urma make -j4 # 强制使用 mock,或强制使用真实 liburma sh config_brpc.sh --headers=/usr/include --libs=/usr/lib \ --with-urma --with-urma-mock sh config_brpc.sh --headers=/usr/include --libs=/usr/lib \ --with-urma --without-urma-mock
默认根据 liburma 是否存在自动选择;--without-urma-mock 要求配置路径中 存在 liburma。
# 编译带 URMA 支持的 brpc bazel build --define=BRPC_WITH_URMA=true //:brpc # 仅展示关闭下载的开关;头文件仍须由工具链或本地 BUILD 目标声明 bazel build --define=BRPC_WITH_URMA=true \ --define=BRPC_DOWNLOAD_URMA_HEADERS=false //:brpc # 编译并运行 URMA 单元测试(使用 mock,不需要 URMA 硬件) bazel test --define=BRPC_WITH_URMA=true \ --define=BRPC_URMA_USE_MOCK=true //test:brpc_unittests # 使用真实 liburma;要求链接器可找到 -lurma bazel build --define=BRPC_WITH_URMA=true \ --define=BRPC_URMA_USE_MOCK=false //:brpc
Bazel 会自动获取固定 commit 的 UMDK 头文件,并通过 @umdk//:urma_headers 提供给 URMA 目标。设置 BRPC_DOWNLOAD_URMA_HEADERS=false 后,Bazel 不再依赖 该外部仓库;实际构建仍需通过工具链或本地 BUILD 目标声明 urma_api.h、urma_types.h 和 urma_ubagg.h。仅通过 --copt=-I/path 指向工作区外目录,在默认 Bazel 沙箱下不会把未声明的头文件 带入编译输入。设置 BRPC_URMA_USE_MOCK=true 强制加入 mock;设置为 false 则排除 mock 并链接 liburma。未指定时,URMA 构建默认使用 mock。
通过在 channel / server 上设置 socket_mode 选择传输层:
// 客户端 brpc::ChannelOptions opt; opt.socket_mode = brpc::SOCKET_MODE_URMA; opt.protocol = "baidu_std"; // URMA 仅支持 baidu_std brpc::Channel channel; channel.Init("127.0.0.1:8003", &opt); // 服务端 brpc::ServerOptions sopt; sopt.socket_mode = brpc::SOCKET_MODE_URMA; server.Start(port, &sopt);
若对端不支持 URMA(例如 TCP 客户端连接 URMA 服务端),在 4 字节 magic 握手后透明回退到 TCP,应用代码无需改动。
UrmaTransport 沿用与 RdmaTransport / UBShmTransport 一致的两层设计:
UrmaTransport : public Transport (urma_transport.{h,cpp})
+-- std::shared_ptr<TcpTransport> (回退路径)
+-- urma::UrmaEndpoint* (URMA 数据路径)
+-- UrmaState { URMA_ON, URMA_OFF, URMA_UNKNOWN }
urma::UrmaEndpoint : public SocketUser (urma/urma_endpoint.{h,cpp})
+-- UrmaResource { jfc, jfce, jfr, jetty, remote_jetty, remote_seg }
+-- 握手状态机(C/S 对称,在 TCP fd 上驱动)
+-- 发送路径:urma_post_jetty_send_wr(URMA_OPC_SEND)
+-- 接收路径:urma_poll_jfc -> HandleCompletion -> InputMessenger
+-- 双窗口信用流控(_remote_rq_window_size / _sq_window_size)
与 RDMA / UBRing 一致,控制面为 TCP,数据面为 URMA:
UrmaConnect::StartConnect 起客户端握手 bthread。UrmaHello 消息(v2 二进制 magic URMA, v3 protobuf magic URM3),携带本地 EID、jetty id、recv buffer 数量、 以及扁平化的 buffer 池 segment。urma_import_seg 再调用 urma_import_jetty,为远端 EID 建立传输路径(TP)路由。跳过 import_seg 会导致首个 SEND 被硬件以 URMA_CR_RNR_RETRY_CNT_EXC_ERR 拒绝。HELLO_ACK_URMA_OK = 0x1)确认双方均要 URMA。申请一大段 mmap 内存,用 urma_register_seg 一次性注册,再切成固定大小 buffer(默认 8KB)。劫持 butil::iobuf::blockmem_allocate 使每个 IOBuf block 都由注册 segment 支撑,发送路径可直接从 IOBuf block refs 构建 urma_sge_t,无需逐消息注册(与 RDMA block_pool 设计一致)。用户注册 内存通过 urma::RegisterMemoryForUrma / DeregisterMemoryForUrma 支持。
所有 flag 使用 urma_ 前缀(对标 RDMA 的 rdma_ 前缀):
| Flag | 默认 | 用途 |
|---|---|---|
--urma_use_polling | false | 轮询 JFC 而非事件模式 |
--urma_poller_num | 1 | 每 bthread tag 的轮询器数(轮询模式) |
--urma_disable_bthread | false | 内联处理消息(不起 bthread) |
--urma_sq_size | 128 | 本地 JFS 深度 [16, 4096] |
--urma_rq_size | 128 | 本地 JFR 深度 [16, 4096] |
--urma_cqe_poll_once | 32 | 每次 urma_poll_jfc 的上限 |
--urma_recv_zerocopy | true | 大于 --urma_zerocopy_min_size 的接收零拷贝 |
--urma_zerocopy_min_size | 512 | 小于此值的接收拷贝 |
--urma_device | "" | URMA 设备名(空=首个) |
--urma_max_sge | 0 | 每 WR SGE 上限(0=设备上限) |
--urma_bonding_mode | 0 | bonding 模式:0=standalone,1=active-backup,2=balance |
--urma_bonding_level | 0 | bonding 层级:0=IODIE,1=port |
--urma_prepared_jetty_cnt | 8 | 预连接 Jetty+CQ 请求数量;会根据 RLIMIT_NOFILE 自动限制 |
--urma_buffer_size | 8192 | 池中每个 buffer 大小(字节) |
--urma_buffer_count | 65536 | 池中 buffer 数量 |
--urma_poller_yield | false | 忙轮询循环中主动让出 bthread |
--urma_client_handshake_version | 2 | 客户端握手版本(2=二进制,3=protobuf) |
设备名以 bonding 开头时,brpc 会在创建 context 后、创建 segment 和队列 前配置 provider。默认 standalone+IODIE 配置与 UMDK 性能工具保持一致。 bonding 支持需要 provider 扩展头文件 urma_ubagg.h。
UrmaTransport 推荐用于大包和跨节点高吞吐路径,而 UBRing (SOCKET_MODE_UBRING)对小包和同机 IPC最优(亚微秒、零系统调用)。 混合负载可按服务选择传输层:
| 场景 | 建议 socket_mode |
|---|---|
| 同机 IPC | SOCKET_MODE_UBRING |
| 跨节点小包(< 64KB) | SOCKET_MODE_UBRING(UBS-Mem)或 SOCKET_MODE_URMA |
| 跨节点大包(>= 64KB) | SOCKET_MODE_URMA |
| 传统 RoCE/IB 数据中心 | SOCKET_MODE_URMA 或 SOCKET_MODE_RDMA |
单连接内按包大小自动分流的方案(路线 C 方案 B)作为后续演进方向。
baidu_std 协议(与 RDMA 一致)。SSL、RTMP、NSHEAD、MONGO 在 ContextInitOrDie 阶段拒绝。liburma。