RDMA在典型场景下的技术应用分析与探索

本文分析了RDMA在典型场景下的技术应用。介绍业务适配RDMA的类型,以Redis为例分析改造方案,进行Redis RDMA测试。还探讨开源程序基于RDMA的方案,如Tensorflow、Brpc、NCCL等,以及Libvma和SMC - R方式。最后总结RDMA改造要点及内存管理模式。

本文首发于:RDMA在典型场景下的技术应用分析与探索

 

1.业务适配RDMA类型

RDMA传输的适配,从业务场景的使用角度来看,大致可分为如下几种类型。

场景一:机器学习、分布式存储等场景,使用社区成熟的方案,如在机器学习场景中使用的NCCL、Tensorflow等框架中都适配了多种传输方式(包含tcp、rdma等),块存储Ceph中也同时支持tcp及rdma两种通信模式,这种业务场景下业务侧更多关注的是配置及使用,在IAAS基础设施侧将RDMA环境准备好后,使能框架使用rdma的传输模式即可。

场景二:业务程序使用类似于RPC远程调用的通信方式,业务侧需要将原有使用的RPC(大部分是GRPC)调用改为ORPC调用,在这种场景下业务和传输更像是两个独立的模块,通过SDK的方式进行调用,所以适配起来改造的代码并不多,通常是业务层面修改调用RPC的接口方式。但由于业务方可能使用多种编程语言,RPC over RDMA需要进行编程语言进行适配。

场景三:业务程序通信是私有化通信,比如使用socket套接字结合epoll完全自有实现的一套通信机制。这种场景下其实改造也区分情况,即业务IO与网络IO是否耦合,若比较解耦,代码中抽象出一层类似于最新Redis代码中ConnectionType这样的架构[2],那么只需要实现一套基于RDMA通信且符合Redis ConnectionType接口定义的新传输类型即可,改造量相对可控并且架构上也比较稳定;而若业务IO与网络IO结合的较为紧密的情况下,这种场景下往往改造起来会比较复杂,改造的时候需要抽丝剥茧的找出业务与网络之间的边界,再进行网络部分的改造。

2.Redis RDMA改造方案分析

首先,以Redis改造为RDMA传输为例,分析基于RDMA传输的应用程序改造逻辑与流程。

第一步是需要梳理出来Redis中与网络传输相关的逻辑,这部分有比较多的参考资料,这里简单总结一下。

Redis中实现了一套Reactor模式的事件处理逻辑名为AE,其主要流程为:

1、使用epoll等机制监听各文件句柄,包括新建连接、以及已建立的连接等;

2、根据事件的不同调用对应的事件回调处理;

3、循环进行epoll loop并进行处理。

参考[2]中分析了当前redis的连接管理是围绕connection这个对象进行管理(可类比socket套接字的管理),抽象一层高于socket的connection layer,以便兼容不同的传输层,各个字段解释如下。

type:各种连接类型的回调接口,定义了诸如事件回调、listen、accept、read、write等接口,类比tcp socket实现的proto_ops。

state:当前连接的状态,如CONNECTING/ACCEPTING/CONNECTED/CLOSED等状态,类比TCP的状态管理。

fd:连接对应的文件句柄。

iovcnt:进行iov操作的最大值。

private_data:保存私有数据,当前存放的是redis中client的指针。

conn_handler/write_handler/read_handler:分别对应连接connect、write、read时的处理接口。

get_type: connection的连接类型,当前redis已支持tcp、unix、tls类型,返回字符串。

init:在每种网络连接模块注册时调用,各模块私有初始化,如tcp、unix类型当前未实现,tls注册时做了一些ssl初始化的前置工作。

ae_handler: redis中的网络事件处理回调函数,redis中使用aeCreateFileEvent为某个fd及事件注册处理函数为ae_handler,当redis的主循环aeMain中发现有响应的事件时会调用ae_handler进行处理,如在tcp连接类型中ae_handler为connSocketEventHandler,该函数分别处理了链接建立、链接可读、链接可写三种事件。

listen: 监听于某个IP地址和端口,在tcp连接类型中对应的函数为connSocketListen,该函数主要调用bind、listen。

accept_handler: redis作为一个服务端,当接收到客户端新建连接的请求时候的处理函数,一般会被.accept函数调用,比如在tcp连接类型中,connSocketAccept调用accept_handler,该方法被注册为connSocketAcceptHandler,主要是使用accept函数接收客户端请求,并调用acceptCommonHandler创建client。

addr: 返回连接的地址信息,主要用于一些连接信息的debug日志。

is_local:返回连接是否为本地连接,redis在protected模式下时,调用该接口判断是否为本地连接进行校验。

conn_create/conn_create_accepted:创建connection,对于tcp连接类型,主要是申请connection的内存,以及connection初始化工作。

shutdown/close:释放connection的资源,关闭连接,当某个redis客户端移除时调用。

connect/blocking_connect:实现connection的非阻塞和阻塞连接方法,在tcp连接类型中,非阻塞连接调用aeCreateFileEvent注册连接的可写事件,继而由后续的ae_handler进行处理,实现非阻塞的连接;而阻塞连接则在实现时会等待连接建立完成。

accept:该方法在redis源码中有明确的定义,可直接调用上述accept_handler,tcp连接类型中,该方法被注册为connScoketAccept。

write/writev/read:和linux下系统调用write、writev、read行为一致,将数据发送至connection中,或者从connection中读取数据至相应缓冲区。

set_write_handler:注册一个写处理函数,tcp连接类型中,该方法会注册connection可写事件,回调函数为tcp的ae_handler。

set_read_handler:注册一个读处理函数,tcp连接类型中,该方法会注册connection可读事件,回调函数为tcp的ae_handler。

sync_write/sync_read/sync_readline:同步读写接口,在tcp连接类型中实现逻辑是使用循环读写。

has_pending_data:检查connection中是否有尚未处理的数据,tcp连接类型中该方法未实现,tls连接类型中该方法被注册为tlsHasPendingData,tls在处理connection读事件时,会调用SSL_read读取数据,但无法保证数据已经读取完成[3],所以在tlsHasPendingData函数中使用SSL_pending检查缓冲区是否有未处理数据,若有的话则交由下面的process_pending_data进行处理。has_pending_data方法主要在事件主循环beforesleep中调用,当有pending data时,事件主循环时不进行wait,以便快速进行下一次的循环处理。

process_pending_data:处理检查connection中是否有尚未处理的数据,tcp连接类型中该方法未实现,tls连接类型中该方法被注册为tlsProcessPendingData,主要是对ssl缓冲区里面的数据进行读取。process_pending_data方法主要在事件主循环beforesleep中调用

get_peer_cert:TLS连接特殊方法。

结合当前代码中tcp及tls实现方法,梳理出和redis connection网络传输相关的流程:

                                               图:Redis Connection C

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值