1. 从“数据搬家”的烦恼说起
如果你在数据中心、高性能计算或者云服务领域工作,大概率听过“RDMA”这个词。它就像一个传说中的“黑科技”,经常和“零拷贝”、“超低延迟”、“高吞吐”这些听起来就很厉害的特性绑定在一起。但很多朋友,包括我早期接触时,都有点懵:它到底是个啥?为什么能这么快?和我们熟悉的TCP/IP网络有什么区别?
简单来说,你可以把传统网络通信想象成两个办公室之间寄送文件。A办公室(应用A)要发一份文件给B办公室(应用B)。流程是这样的:A先得把文件从自己的文件柜(用户内存)里拿出来,交给本楼的快递收发室(操作系统内核)。收发室要登记、打包、贴上快递单(封装TCP/IP包头),然后交给大楼的物流中心(网卡)。物流中心再把包裹送到B大楼的物流中心,B的收发室拆包、登记,最后把文件送到B的文件柜里。这个过程里,文件被“搬运”了多次,A和B的“员工”(CPU)还得花时间处理各种登记手续(系统调用、协议栈处理),效率不高,延迟也大。
而RDMA(Remote Direct Memory Access,远程直接内存访问)干了一件颠覆性的事:它让A办公室的员工,可以直接把文件塞进B办公室指定的文件柜里,中间绕过了两边的收发室和大部分物流手续。A的网卡(现在是一张支持RDMA的智能网卡)直接读取A应用内存中的数据,然后通过网络,直接写入到B应用提前准备好的内存地址中。整个过程,两边的操作系统内核几乎不参与,CPU也不用被打扰去处理网络协议。这就是“零拷贝”(数据不用在内核和用户空间来回拷贝)和“内核旁路”的核心思想。
我第一次在项目中实测RDMA时,那种性能提升是震撼的。一个原本需要毫秒级响应的关键服务,延迟直接降到了微秒级,吞吐量翻了好几倍。但这东西也不是银弹,它有自己特定的适用场景和一套全新的“游戏规则”。今天,我就结合自己踩过的坑和实战经验,带你彻底读懂RDMA,搞清楚它为什么快,以及怎么用。
2. RDMA核心原理:为什么它能“隔空取物”
要理解RDMA,光看比喻不够,我们得深入它的技术内核。它的设计哲学完全不同于传统的Socket编程模型,可以总结为三大核心支柱:零拷贝、内核旁路和传输卸载。
2.1 零拷贝:告别数据“来回折腾”
在传统TCP/IP网络栈中,数据发送需要经历“用户缓冲区 -> 内核缓冲区 -> 网卡缓冲区”的多次拷贝。接收则反过来。每次拷贝都消耗CPU周期和内存带宽。RDMA的零拷贝,是指应用程序的数据缓冲区可以直接被RDMA网卡访问。
实现机制:这依赖于两个关键步骤:
- 内存注册:应用需要先告诉RDMA网卡:“这块内存区域我准备用来收/发数据,请你把它‘管’起来。” 这个过程叫
Memory Registration。网卡会锁定这些物理内存页,并为其生成一个唯一的“钥匙”,叫做lkey(本地密钥)和rkey(远程密钥)。只有持有正确钥匙的远程节点,才能通过RDMA操作访问这块内存。 - 直接数据搬运:注册完成后,当发起读写操作时,RDMA网卡(我们通常叫它HCA, Host Channel Adapter)的DMA引擎,会直接根据操作描述中的地址和钥匙,从本地已注册的内存中读取数据,封装成RDMA报文发出;或者将接收到的数据,直接写入到远程已注册的内存中。全程不经过操作系统内核的缓冲区。
注意:内存注册本身是有开销的(涉及页面锁定、TLB刷新等)。因此,RDMA适合那些需要反复在相同缓冲区进行大量数据传输的场景,一次注册,多次使用,从而摊薄注册开销。对于单次、小数据量的传输,RDMA可能反而更慢。
2.2 内核旁路:给CPU“减负”
内核旁路意味着数据传输路径完全绕过了操作系统内核协议栈(TCP/IP)。应用程序通过用户空间的库(如libibverbs)直接与RDMA网卡驱动对话,发起和完成操作。
工作流程对比:
- 传统Socket:
send()/recv()-> 系统调用陷入内核 -> 协议栈处理 -> 网卡驱动 -> 硬件。 - RDMA Verbs:
ibv_post_send()-> 用户态库将工作请求(Work Request, WR)直接写入网卡的队列 -> 网卡硬件异步处理。
这样做的好处显而易见:
- 低延迟:省去了系统调用和内核协议栈处理的耗时。
- 低CPU占用:CPU不再需要中断处理每个数据包,只需偶尔轮询一下完成队列。在高吞吐场景下,CPU利用率可以从传统的超过50%降到个位数。
- 确定性:减少了操作系统调度、上下文切换带来的延迟抖动,更适合对延迟敏感的应用。
2.3 传输卸载:让网卡变得更“聪明”
这是RDMA网卡硬件能力的体现。传统的网卡(NIC)主要是个“搬运工”,负责帧的收发和简单的校验。而RDMA网卡(HCA)是一个高度集成的“通信处理器”,它在硬件层面实现了可靠的传输协议。
- 协议卸载:像RoCE(RDMA over Converged Ethernet)协议中的重传、确认、流量控制、拥塞控制等逻辑,都在HCA硬件中实现,而不是由主机CPU运行软件协议栈。
- 传输可靠性:RDMA在传输层保证了数据的可靠、按序交付。应用开发者无需像使用UDP那样自己处理丢包和乱序,享受的是类似TCP的可靠性,但性能却是UDP级别的。
这三者结合,共同构成了RDMA高性能的基石。但这也带来了编程模型的根本性变化。
3. RDMA的三种主流传输网络
RDMA是一种规范,它可以通过不同的底层网络实现。目前主流的有三种,它们的选择是项目架构设计的第一步。
3.1 InfiniBand:原生贵族,性能标杆
InfiniBand(IB)是为RDMA而生的网络技术,从硬件交换机、线缆到网卡,是一套完整的生态系统。
- 优势:性能最好,延迟最低(亚微秒级),吞吐量最高(目前可达400Gb/s甚至更高)。它拥有独立的链路层和网络层协议,原生支持RDMA的所有高级特性。
- 劣势:成本高昂,需要专用的IB交换机和线缆(如Mellanox的解决方案),与现有的以太网设施不兼容。通常用于对性能有极致要求的场景,如超级计算机、高端全闪存存储阵列。
- 实战心得:如果你在规划一个新的、预算充足的超算或AI训练集群,IB是首选。但运维团队需要具备IB网络知识,因为它的故障排查工具和思路与以太网有所不同。
3.2 RoCE:以太网上的RDMA,折中之选
RoCE(RDMA over Converged Ethernet)让RDMA跑在了我们熟悉的以太网上。它又分为两个版本:
- RoCE v1:工作在以太网链路层(L2),依赖无损的二层网络。这意味着它不能跨路由器,通常局限于单个数据中心或单个VLAN内。配置需要启用以太网流控(如PFC, 优先级流量控制)来保证零丢包,否则一旦丢包,性能会急剧下降。
- RoCE v2:将RDMA报文封装在UDP/IPv4或IPv6中,可以跨三层网络路由。这是目前更主流的方案,因为它能更好地融入现有的IP网络架构。但它同样需要无损网络或配合ECN(显式拥塞通知)等高级特性来保证稳定性能。
优势:复用现有以太网基础设施,成本远低于IB,部署灵活性高。劣势:性能略低于IB(主要是延迟增加几个微秒),并且对网络质量要求极高。一个配置不当的“有损”以太网,会让RoCE性能变得还不如TCP。
踩坑记录:我们早期部署RoCE v2时,曾因为交换机上的ECN和PFC配置不匹配,导致在流量突发时出现严重的吞吐波动和延迟尖峰。排查过程非常痛苦,需要网络团队和系统团队紧密协作。强烈建议:在生产环境部署RoCE前,必须在真实流量模型下进行充分的网络验证和压力测试。
3.3 iWARP:TCP/IP上的RDMA,兼容性王者
iWARP(Internet Wide Area RDMA Protocol)将RDMA承载在标准的TCP协议之上。
- 优势:兼容性最好。因为它基于TCP,所以可以穿越标准的IP路由器和防火墙,对底层网络没有“无损”的苛刻要求。部署最简单,理论上可以在广域网上使用。
- 劣势:性能是三种中最差的。因为TCP的复杂处理(虽然部分可卸载)仍然会带来比RoCE和IB更高的延迟和CPU开销。硬件支持厂商也相对较少。
如何选择?你可以参考这个简单的决策流:
- 追求极致性能,不计成本-> 选InfiniBand。
- 追求高性能,且拥有或愿意构建可控的无损以太网环境(如云数据中心内部)-> 选RoCE v2。
- 需要跨标准IP网络通信,对性能要求不是最极端,希望部署最简单-> 选iWARP。
- 如果只是想在同一个机架或交换机下做实验->RoCE v1或 v2 都可以。
目前业界,特别是在云计算和AI训练领域,RoCE v2 因其良好的平衡性,成为了最受关注和广泛部署的方案。
4. RDMA编程模型核心:队列与操作
理解了底层网络,我们再看上层怎么用。RDMA的编程接口(Verbs API)核心是围绕队列对展开的,这是一种生产者-消费者模型。
4.1 核心概念:队列对、完成队列与内存区域
- 队列对:每个通信连接由一个队列对表示。一个QP包含两个队列:
- 发送队列:应用把想要执行的操作(发送、RDMA写、RDMA读等)描述成一个工作请求,放入SQ。
- 接收队列:应用提前放好一些工作请求,用来告诉网卡“收到数据后应该放到哪里”。这对于接收传统的发送/接收操作是必须的。
- 完成队列:SQ和RQ共享一个或两个完成队列。当网卡处理完一个工作请求(无论成功失败),就会产生一个完成事件,放入CQ。应用通过轮询CQ来获知操作完成情况。
- 内存区域:就是前面提到的,通过
ibv_reg_mr注册的那块内存。它是所有RDMA操作的数据来源或目的地。
工作流程简化版:
- 应用注册内存区域。
- 创建QP和CQ,并将它们关联。
- 建立连接(通过Out-of-Band的方式,如TCP Socket,交换QP信息)。
- 应用向RQ投递接收WR(准备接球)。
- 应用向SQ投递发送WR(发球)。
- 网卡异步处理:执行SQ的发送操作;远端执行后,本地网卡处理RQ的接收操作。
- 应用轮询CQ,看到发送完成和接收完成的条目。
- 循环4-7步。
4.2 五种核心操作模式
RDMA定义了不同的服务类型,对应不同的QP类型,影响着消息的可靠性和有序性。
| QP类型 | 可靠性 | 有序性 | 适用场景 |
|---|---|---|---|
| 可靠连接 | 保证送达,丢包重传 | 保证顺序 | 最常用,如存储、数据库集群通信 |
| 不可靠连接 | 不保证送达,丢包不重传 | 保证顺序 | 对延迟极度敏感,可容忍偶尔丢包,如金融行情 |
| 可靠数据报 | 保证送达 | 不保证顺序 | 较少使用 |
| 不可靠数据报 | 不保证送达 | 不保证顺序 | 多播/广播场景 |
| 原始数据报 | 无 | 无 | 特殊硬件测试 |
选择建议:新手和大多数应用从可靠连接开始,它提供了类似TCP的语义,最不容易出错。不可靠连接性能最好,但需要应用层能处理消息丢失。
4.3 三种数据操作语义
这是RDMA最强大的地方,它提供了比“发送/接收”更灵活的数据搬运方式。
- 发送/接收:最像传统网络通信。发送方明确发出数据,接收方必须提前发布接收请求来“等待”数据。需要双方协调,是双向通信的基础。
- RDMA写:单向操作。发起方(Initiator)可以直接将数据写入远端(Target)的指定内存地址。远端应用完全感知不到这次写入的发生,直到发起方通过其他方式(例如再发一个Send消息)通知它。这是实现“一端主动推送”模式的关键,在存储系统中非常常见(客户端直接写数据到服务器内存)。
- RDMA读:单向操作。发起方可以从远端的内存地址读取数据到本地内存。同样,远端无感知。这常用于“一端主动拉取”模式。
RDMA写/读的威力:它们只需要一次网络往返(对于读,需要请求和响应)就能完成大量数据的传输,并且远端CPU不参与。例如,在分布式存储中,客户端可以直接将数据块RDMA写入到服务端的内存,然后发一个小的Send消息通知“数据已就绪”。服务端CPU只在处理通知消息时被唤醒,极大地提升了效率。
5. 实战:构建一个简单的RDMA应用
理论说了这么多,我们动手写一个最简单的“可靠连接 + 发送/接收”模式的回显服务端和客户端,看看代码骨架。这里以Linux下的libibverbs库为例。
5.1 环境准备与依赖安装
首先,你需要有支持RDMA的网卡(如Mellanox ConnectX系列)并安装驱动。然后安装用户态开发包:
# 对于Ubuntu/Debian sudo apt-get install libibverbs-dev libibverbs1 librdmacm-dev librdmacm1 ibverbs-utils # 对于RHEL/CentOS sudo yum install libibverbs-devel libibverbs librdmacm-devel librdmacm rdma-core工具集ibv_开头的命令可以用来检查设备状态,例如ibv_devices列出设备,ibv_devinfo查看详细信息。
5.2 服务端代码骨架解析
下面是一个极度简化的服务端步骤,忽略了大量错误处理以突出主流程:
#include <rdma/rdma_cma.h> int main() { struct rdma_cm_id *listen_id, *conn_id; struct ibv_pd *protection_domain; struct ibv_cq *completion_queue; struct ibv_qp_init_attr qp_init_attr; struct ibv_mr *memory_region; char *buffer; // 1. 创建RDMA CM事件通道和监听ID struct rdma_event_channel *ec = rdma_create_event_channel(); rdma_create_id(ec, &listen_id, NULL, RDMA_PS_TCP); // 2. 绑定地址并开始监听 rdma_bind_addr(listen_id, (struct sockaddr*)&server_addr); rdma_listen(listen_id, 10); // 3. 等待并接受连接请求 rdma_get_cm_event(ec, &event); // 等待RDMA_CM_EVENT_CONNECT_REQUEST conn_id = event->id; rdma_ack_cm_event(event); // 4. 为连接创建核心资源 protection_domain = ibv_alloc_pd(conn_id->verbs); // 保护域 completion_queue = ibv_create_cq(conn_id->verbs, 10, NULL, NULL, 0); // 完成队列 buffer = malloc(BUFFER_SIZE); memory_region = ibv_reg_mr(protection_domain, buffer, BUFFER_SIZE, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE); // 注册内存 // 5. 创建队列对并连接到远程 memset(&qp_init_attr, 0, sizeof(qp_init_attr)); qp_init_attr.cap.max_send_wr = qp_init_attr.cap.max_recv_wr = 10; qp_init_attr.cap.max_send_sge = qp_init_attr.cap.max_recv_sge = 1; qp_init_attr.qp_type = IBV_QPT_RC; // 可靠连接 qp_init_attr.send_cq = qp_init_attr.recv_cq = completion_queue; rdma_create_qp(conn_id, protection_domain, &qp_init_attr); // 6. 准备接收请求(至关重要!) struct ibv_recv_wr recv_wr, *bad_wr; struct ibv_sge recv_sge; // ... 填充recv_sge (指向memory_region), recv_wr... ibv_post_recv(conn_id->qp, &recv_wr, &bad_wr); // 7. 回复连接建立完成 rdma_accept(conn_id, NULL); // 8. 进入事件循环:轮询CQ,处理接收到的消息,然后回显 while(1) { struct ibv_wc wc; int ne = ibv_poll_cq(completion_queue, 1, &wc); if (ne > 0 && wc.status == IBV_WC_SUCCESS) { if (wc.opcode == IBV_WC_RECV) { // 收到了数据,buffer中 now has data // 准备一个发送WR,将buffer中的数据发回去(回显) struct ibv_send_wr send_wr, *bad_send_wr; struct ibv_sge send_sge; // ... 填充send_sge, send_wr... ibv_post_send(conn_id->qp, &send_wr, &bad_send_wr); // 重新投递一个接收WR,准备接收下一条消息 ibv_post_recv(conn_id->qp, &recv_wr, &bad_wr); } // ... 处理发送完成事件等 } } // 清理资源... return 0; }5.3 客户端代码关键步骤
客户端与服务端类似,区别在于它主动发起连接:
// 1. 创建CM ID和资源(PD, CQ, MR等),与服务端步骤4类似 // 2. 解析服务器地址后,主动发起解析和连接 rdma_resolve_addr(cm_id, NULL, (struct sockaddr*)&server_addr, 2000); // 等待RDMA_CM_EVENT_ADDR_RESOLVED, RDMA_CM_EVENT_ROUTE_RESOLVED rdma_resolve_route(cm_id, 2000); // 等待RDMA_CM_EVENT_ROUTE_RESOLVED rdma_connect(cm_id, &conn_param); // 发起连接 // 等待RDMA_CM_EVENT_ESTABLISHED,连接建立成功 // 3. 连接建立后,客户端可以开始发送消息 struct ibv_send_wr send_wr, *bad_send_wr; struct ibv_sge sge; // 填充sge(指向本地已注册的buffer),填充send_wr ibv_post_send(cm_id->qp, &send_wr, &bad_send_wr); // 4. 轮询CQ,等待发送完成和接收服务端的回显实操要点:
- 接收必须提前投递:这是RDMA Send/Recv模式最容易出错的地方。在连接建立后、任何发送操作之前,接收方必须至少投递一个Recv WR到RQ,否则对端的Send操作会失败。这就像接球手必须先就位。
- 内存注册开销:尽量复用已注册的内存区域,避免频繁注册/注销。
- CQ轮询策略:高吞吐场景下,持续轮询CQ性能最好。低负载场景可以考虑用事件通知(
ibv_get_cq_event),但会有额外延迟。 - 错误处理:RDMA Verb API的每个调用都可能失败,实际代码中必须检查每一个返回值。
wc.status提供了操作完成的状态,需要仔细处理。
6. RDMA的典型应用场景与选型思考
理解了怎么用,我们再看用它来做什么。RDMA不是万能的,它在特定场景下才能发挥最大威力。
6.1 分布式存储与并行文件系统
这是RDMA的“杀手级”应用。
- 场景:Ceph, GlusterFS, WekaIO, DAOS等。客户端需要高速读写存储服务器上的数据。
- RDMA价值:
- 客户端写:客户端可以直接将数据块通过RDMA写入存储服务器的内存中,极大减少服务器CPU开销,让CPU专注于元数据和IO调度。
- 客户端读:存储服务器可以将数据准备好后,通知客户端直接通过RDMA读取,或者主动推送给客户端。
- 节点间数据同步:在副本间同步数据时,使用RDMA可以加速数据传输过程。
- 实战配置:通常采用RoCE v2网络,存储服务器配备高性能NVMe SSD和高速RDMA网卡。需要精细调整QP深度、CQ大小,并可能使用多QP并行来提升吞吐。
6.2 高性能计算与AI训练
- 场景:MPI(消息传递接口)集群中节点间的通信,大规模深度学习训练中参数服务器与Worker之间、或Worker之间的梯度同步。
- RDMA价值:将All-Reduce、Broadcast等集合通信操作的延迟降到最低,直接决定了大规模训练任务的扩展效率。NVIDIA的NCCL库就深度优化并利用了InfiniBand和RoCE的RDMA能力。
- 选型:顶级超算和AI集群通常采用InfiniBand。大型云服务商内部的AI训练集群则广泛部署RoCE v2。
6.3 数据库与内存网格
- 场景:分布式数据库(如Google Spanner的跨数据中心同步)、内存数据库(如Redis集群)、内存数据网格(如Apache Ignite)。
- RDMA价值:实现极低延迟的远程内存访问。一个节点可以像访问本地内存一样,通过RDMA读/写另一个节点的内存中的数据页,这对于实现高效的分布式缓存和一致性协议至关重要。
- 注意事项:这需要非常小心地设计数据结构和并发控制,因为RDMA绕过了远程CPU,传统的锁机制可能失效,常需要依赖原子操作(如CAS)的扩展。
6.4 虚拟化与云原生
- 场景:云平台的虚拟网络(如OVS-DPDK)、容器网络(如Kubernetes的Pod间通信)、以及分离式存储(如计算节点通过NVMe-oF访问远程存储)。
- RDMA价值:通过SR-IOV技术,将物理RDMA网卡虚拟化成多个虚拟函数直接透传给虚拟机或容器,使其能直接享受RDMA的高性能,避免宿主机虚拟交换机的性能损耗。NVMe-oF over RDMA更是提供了接近本地NVMe SSD的远程存储访问体验。
7. 常见问题、性能调优与避坑指南
RDMA性能虽高,但调优和排错门槛也高。这里分享一些实战中积累的经验。
7.1 性能调优关键参数
| 参数 | 影响 | 调优建议 |
|---|---|---|
| QP深度 | 决定了SQ和RQ中未完成WR的数量。深度不足会导致流水线断流,影响吞吐。 | 根据应用飞行中的消息数设置。高吞吐应用通常需要较大的深度(如256-1024)。但深度越大,消耗的硬件资源越多。 |
| CQ大小 | 存放完成事件的数量。如果CQ满了,新的完成事件会丢失,导致严重错误。 | 设置为至少(发送队列深度+接收队列深度)的2倍以上,以防突发完成事件。 |
| SGE数量 | 单个WR可以指向的分散/聚集内存段数量。 | 如果你的数据在内存中不连续,增加SGE数量可以避免额外的内存拷贝。通常1-2个就够,复杂场景可增加。 |
| 内联数据大小 | 小数据可以直接放在WQE(工作队列元素)中发送,无需额外DMA读取,降低延迟。 | 对于大量的小消息(如64字节以下),启用并设置合适的内联大小能显著提升性能。 |
| 轮询 vs 事件 | CQ完成通知方式。轮询延迟低但占CPU;事件通知CPU友好但延迟高。 | 延迟敏感型应用使用忙等待轮询。吞吐型或延迟不敏感应用可使用事件通知。 |
7.2 典型问题与排查思路
问题1:连接建立失败
- 排查:检查
rdma_cm事件。常见原因:防火墙阻止了端口(默用的端口,RoCE v2用UDP 4791),两端QP类型不匹配,或交换机的PFC/ECN配置错误导致RoCE协商失败。使用ibstat,ibv_devinfo查看网卡状态,用ethtool查看网卡流控设置。
问题2:Send操作失败,返回IBV_WC_RETRY_EXC_ERR
- 排查:这通常是对端没有提前投递Recv WR导致的。请务必确认你的通信模式,确保接收方在发送方发出Send之前,已经
ibv_post_recv。这是新手最常犯的错误。
问题3:RDMA写/读操作失败,返回权限错误
- 排查:检查内存区域的注册权限。RDMA写操作要求本地MR具有
IBV_ACCESS_LOCAL_WRITE权限,而远程要写入你的内存,你的MR必须具有IBV_ACCESS_REMOTE_WRITE权限(对于读则是IBV_ACCESS_REMOTE_READ)。同时,建立连接时交换的rkey必须正确。
问题4:性能达不到预期,吞吐低,延迟高
- 排查:这是一个系统性问题。
- 网络层面:对于RoCE,用
ping看基础延迟,用iperf3看TCP带宽作为基准。用rdma工具(如ib_write_bw,ib_send_lat)测试RDMA极限性能。如果RDMA性能远差于TCP,基本是网络配置问题(PFC/ECN)。 - 主机层面:检查CPU是否因为轮询CQ而跑满一个核?检查PCIe带宽是否成为瓶颈(用
lspci -vv查看链路速度)。检查内存带宽。 - 应用层面:QP深度是否足够?是否在频繁注册/注销内存?CQ是否溢出?消息大小是否太小,导致协议头开销占比过大?
- 网络层面:对于RoCE,用
问题5:运行一段时间后出现内存泄漏或错误
- 排查:RDMA资源(MR, QP, CQ, PD)必须由应用显式释放。确保所有错误分支都有资源清理代码。使用
ibv_asyncwatch工具可以监控异步错误事件。
7.3 避坑经验总结
- 从“可靠连接”和“发送/接收”模式开始:在熟悉基本流程和调试方法前,不要急于使用不可靠模式或RDMA写/读。
- 重视网络基础设施:尤其是对于RoCE,“无损网络”不是可选项,是必选项。务必与网络团队确认PFC、ECN、MTU(通常需要设置为9000,即巨帧)的配置。
- 工具是你的朋友:熟练掌握
perf、ibv_rc_pingpong、ib_write_bw、rdma系列命令和ethtool,它们是定位性能问题的利器。 - 理解“异步”模型:RDMA操作是异步的,
ibv_post_send只是把任务提交了,不代表完成了。完成与否必须通过轮询或事件从CQ获取。编程模型需要适应这种变化。 - 考虑使用高级封装库:直接使用Verbs API比较繁琐。对于生产应用,可以考虑使用更高级的封装,如
librdmacm简化连接管理,或者像rsocket、FaRMRPC框架、DPDK的RDMA插件等,它们隐藏了部分复杂性。
RDMA是一扇通往高性能网络世界的大门,它用更复杂的编程模型换来了极致的性能提升。对于需要处理海量数据、追求微秒级延迟的应用来说,这项技术正在从超算中心走向更广泛的云计算和存储领域。上手它确实有门槛,但一旦打通,带来的收益是巨大的。我的建议是,先在一个可控的测试环境里,从最简单的ping-pong程序开始,一步步理解每个概念和步骤,再逐渐应用到复杂的业务场景中。