RDMA该怎么用

RDMA该怎么用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

势不可挡的 RDMA

如今,服务器的网络带宽越来越高。当网络带宽迈过万兆这条线后,操作系统用于处理网络IO的开销就越来越难以忽视。在一些网络IO密集的业务中,操作系统本身成为了网络通信的瓶颈,这不仅会导致调用时延的增加(尤其是长尾),还会影响到服务的整体吞吐。

相对于网络带宽的发展速度,CPU性能的发展停滞是导致上述问题的主要原因。 因此,想从根本上解决CPU参与网络传输的低效问题,就要更多地借助专用芯片的能力,RDMA高性能网络势不可挡。 

RDMA(Remote Direct Memory Access),可以简单理解为网卡完全绕过CPU实现两个服务器之间的内存数据交换。其作为一种硬件实现的网络传输技术,可以大幅提升网络传输效率,帮助网络IO密集的业务(比如分布式存储、分布式数据库等)获得更低的时延以及更高的吞吐。

具体来说,RDMA技术的应用要借助支持RDMA功能的网卡以及相应的驱动程序。由下图所示,一旦应用程序分配好资源,其可以直接把要发送的数据所在的内存地址和长度信息交给网卡。网卡从内存中拉取数据,由硬件完成报文封装,然后发送给对应的接收端。接收端收到RDMA报文后,直接由硬件解封装,取出数据后,直接放在应用程序预先指定的内存位置。

RDMA该怎么用

由于整个IO过程无需CPU参与,无需操作系统内核参与,没有系统调用,没有中断,也无需内存拷贝,因此RDMA网络传输可以做到极高的性能。在极限benchmark测试中,RDMA的时延可以做到1us级别,而吞吐甚至可以达到200G。

RDMA技术说明

需要注意的是,RDMA的使用需要应用程序的代码配合(RDMA编程)。与传统TCP传输不同,RDMA并没有提供socket API封装,而是要通过verbs API来调用(使用libibverbs)。出于避免中间层额外开销的考虑,verbs API采用了贴近硬件实现的语义形态,导致使用方法与socket API差异巨大。 因此,对大多数开发者来说,无论是改造原有应用程序适配RDMA,还是写一个全新的RDMA原生的应用程序,都不容易。

RDMA 编程难在哪里?

如图所示,在socket API中,发送接收数据主要用到的接口如下:

RDMA该怎么用

Socket API

其中,write和read操作中的fd是标识一个连接的文件描述符。应用程序要发送的数据,会通过write拷贝到系统内核缓冲区中;而read实际上也是从系统内核缓冲区中将数据拷贝出来。 在绝大多数应用程序里,通常都会设置fd为非阻塞的,也就是说,如果系统内核缓冲区已满,write操作会直接返回;而如果系统内核缓冲区为空,read操作也会直接返回。 为了在第一时间获知内核缓冲区的状态变化,应用程序需要epoll机制来监听EPOLLIN和EPOLLOUT事件。如果epoll_wait函数因这些事件返回,则可以触发下一次的write和read操作。这就是socket API的基本使用方法。 作为对比,在verbs API中,发送接收数据主要用到的接口如下:

RDMA该怎么用

Verbs API

其中,ibv_是libibverbs库中函数和结构体的前缀。ibv_post_send近似于发送操作,ibv_post_recv近似于接收操作。发送接收操作里面的qp(queue pair)近似于socket API里面的fd,作为一个连接对应的标识。wr(work request)这个结构体里包含了要发送/接收的数据所在的内存地址(进程的虚拟地址)和长度。ibv_poll_cq则作为事件检测机制存在,类似于epoll_wait。

乍一看去,RDMA编程似乎很简单,只要把上述函数替换了就可以。但事实上,上述的对应关系都是近似、类似,而不是等价。 关键区别在于,socket API都是同步操作,而RDMA API都是异步操作(注意异步和非阻塞是两个不同的概念)

具体而言,ibv_post_send函数返回成功,仅仅意味着成功地向网卡提交了发送请求,并不保证数据真的被发送出去了。如果此时立马对发送数据所在的内存进行写操作,那么发送出去的数据就很可能是不正确的。socket API是同步操作,write函数返回成功,意味着数据已经被写入了内核缓冲区,虽然此时数据未必真的发送了,但应用程序已经可以随意处置发送数据所在的内存。

RDMA该怎么用