解鎖tRPC高性能密碼:網(wǎng)絡(luò)方案簡(jiǎn)介!

導(dǎo)語(yǔ)?|?本文介紹了部分高性能網(wǎng)絡(luò)方案,包括RDMA、HARP、io_uring等。從技術(shù)原理、落地可行性等方面,簡(jiǎn)要地做出分析,希望能對(duì)此方面感興趣的開(kāi)發(fā)者提供一些經(jīng)驗(yàn)和幫助。
一、背景
業(yè)務(wù)中經(jīng)常會(huì)有這樣的場(chǎng)景:
隨著網(wǎng)卡速率的提升(10G/25G/100G),以及部分業(yè)務(wù)對(duì)低延遲的極致追求(1ms/50us),目前的內(nèi)核協(xié)議棧由于協(xié)議復(fù)雜、流程復(fù)雜、設(shè)計(jì)陳舊等因素,已經(jīng)逐漸成為業(yè)務(wù)瓶頸。
業(yè)界已經(jīng)有部分RDMA、DPDK的實(shí)踐,但是對(duì)于大多數(shù)開(kāi)發(fā)者而言,依然比較陌生。
那么這些方案各自的場(chǎng)景究竟怎樣?是否能夠?yàn)楦嗟臉I(yè)務(wù)賦能?以下是階段性簡(jiǎn)要總結(jié)。
二、RDMA
(一)原理簡(jiǎn)介
相對(duì)于傳統(tǒng)的網(wǎng)絡(luò)協(xié)議棧,RDMA提供的關(guān)鍵特性即為:Kernel Bypass,也即利用專用的NIC(網(wǎng)卡)進(jìn)行硬件層面的協(xié)議傳輸、編解碼(Offload),通過(guò)內(nèi)存映射技術(shù)直接與用戶態(tài)程序交互,從而避免了復(fù)雜低效的內(nèi)核中介。
基于這種設(shè)計(jì),隨之提供幾個(gè)額外的重要特性:
Zero-Copy:基于DMA操作,通信全程沒(méi)有額外的CPU介入拷貝,從而降低CPU消耗。
穩(wěn)定低延遲:由于硬件通路的可靠性,從而保證了穩(wěn)定的通信延遲。
多種傳輸模式:RC、RD、UC、UD等?;诓煌瑯I(yè)務(wù)的不同可靠性和性能需求,提供類似TCP/UDP的多種傳輸模式。

由于RDMA定位為高性能網(wǎng)絡(luò)傳輸,同時(shí)也為了簡(jiǎn)化硬件的設(shè)計(jì),一般來(lái)說(shuō),RDMA會(huì)避免如軟件TCP那樣復(fù)雜的可靠性設(shè)計(jì),而是極其依賴底層傳輸網(wǎng)絡(luò)的可靠性。
根據(jù)不同的傳輸網(wǎng)絡(luò),RDMA的具體實(shí)現(xiàn)分為幾類:

另外補(bǔ)充說(shuō)明:
雖然RoCE v1/2依賴融合融合以太網(wǎng),也即無(wú)損傳輸,不過(guò)也有部分廠商的優(yōu)化實(shí)現(xiàn),可以減輕對(duì)無(wú)損傳輸?shù)囊蕾嚒?/span>
Linux kernel 4.9+中,實(shí)現(xiàn)了Soft-RoCE,也即軟件版本的RoCE v2,主要用于測(cè)試、學(xué)習(xí)。
(二)RoCE v2 v.s. iWARP
在以太網(wǎng)環(huán)境,主要可選項(xiàng)為RoCE v2和iWARP,相關(guān)對(duì)比如下:

目前來(lái)看,目前的機(jī)房網(wǎng)絡(luò)建設(shè)中,對(duì)RoCE v2的支持更好,而iWARP卻仍然處于相對(duì)空白的狀態(tài)。
為此,當(dāng)前的調(diào)研主要針對(duì)RoCE v2,而iWARP仍然有待探索。
(三)業(yè)務(wù)落地
后臺(tái)業(yè)務(wù)主流協(xié)議仍然是TCP,具有運(yùn)行穩(wěn)定、調(diào)試工具豐富等優(yōu)勢(shì)。不過(guò)對(duì)于少數(shù)期望高性能的業(yè)務(wù),RDMA也是值得考慮的。
業(yè)務(wù)使用RDMA主要面臨兩方面的困難:
RoCE v2無(wú)損網(wǎng)絡(luò)的要求導(dǎo)致難以跨機(jī)房傳輸,當(dāng)前騰訊機(jī)房的支持為module內(nèi)傳輸(如5跳之內(nèi))。
全新的開(kāi)發(fā)接口如libverbs、UCX等,業(yè)務(wù)軟件需要進(jìn)行適配。
而有些存儲(chǔ)業(yè)務(wù)依賴多副本,網(wǎng)絡(luò)傳輸需要能夠跨越MAN,甚至跨城市傳輸。這直接導(dǎo)致RoCE v2難以落地。
三、io_uring/socket
(一)原理簡(jiǎn)介
io_uring是Linux 5.1+中支持的異步IO框架,其核心優(yōu)勢(shì)有:
真正的異步化設(shè)計(jì)(Proactor),而非如epoll等本質(zhì)上的同步行為(Reactor)。而其關(guān)鍵在于,程序和kernel通過(guò)SQ/CQ兩個(gè)隊(duì)列進(jìn)行解耦。
統(tǒng)一的異步IO框架,不僅支持存儲(chǔ)、網(wǎng)絡(luò)。由于良好的擴(kuò)展性,甚至可以支持任何的系統(tǒng)調(diào)用,如openat、stat等。

如前述,一個(gè)io_uring的實(shí)例,會(huì)建立一對(duì)內(nèi)核和用戶程序共享的隊(duì)列,也即提交隊(duì)列SQ和完成隊(duì)列CQ,兩者皆為SPSC范型:
SQ:用戶態(tài)線程生產(chǎn),然后系統(tǒng)調(diào)用(io_uring_enter)通知內(nèi)核(io_wq kernel thread)消費(fèi)。其中元素稱為SQE。
CQ:內(nèi)核生產(chǎn),然后通知(若用戶程序睡眠等待則喚醒)用戶態(tài)消費(fèi)。其中元素稱為CQE。
這其實(shí)是最常規(guī)也是最經(jīng)典的異步模型,在眾多異步設(shè)計(jì)中可見(jiàn)。
一般情況下,CQE和SQE一一對(duì)應(yīng),不過(guò)io_uring支持multi-shot模式后則不一定如此。
另外,io_uring支持批量生產(chǎn)和消費(fèi),也即連續(xù)生產(chǎn)多個(gè)SQ后,一次性通知內(nèi)核,或者持續(xù)消費(fèi)CQ直到其空。
為了進(jìn)一步優(yōu)化部分場(chǎng)景的性能,io_uring支持眾多的高級(jí)特性:
File Registration:在反復(fù)操作同一個(gè)fd時(shí),加速其查找映射。
Buffer Registration:在read/write等反復(fù)需要在內(nèi)核和用戶程序交換數(shù)據(jù)的場(chǎng)景,可以重復(fù)利用預(yù)注冊(cè)的一批內(nèi)存。
Automatic Buffer Selection:為Proactor read預(yù)注冊(cè)一批內(nèi)存,在就緒后內(nèi)核自動(dòng)選擇其中一塊存放數(shù)據(jù),從而減少內(nèi)存分配釋放,也節(jié)約內(nèi)存資源。
SQ Polling:使內(nèi)核(io_wq)輪詢SQ指定時(shí)間才睡眠,從而減少通知的系統(tǒng)調(diào)用。
IO Polling:開(kāi)啟子系統(tǒng)(存儲(chǔ)、網(wǎng)絡(luò)等)的輪詢模式(需要設(shè)備驅(qū)動(dòng)支持),從而加速部分高速設(shè)備。另外可以配合io_uring_enter(flag:IORING_ENTER_GETEVENTS)進(jìn)行忙等。
Multi-Shot:一次提交,多次完成,如只要一次提交socket accept,后續(xù)連接到來(lái)后多次返回。
io_uring在存儲(chǔ)IO場(chǎng)景,相對(duì)之前的阻塞IO、glibc aio、linux aio等,都有不錯(cuò)的性能提升。
那么在網(wǎng)絡(luò)IO場(chǎng)景呢?是否優(yōu)于epoll等方案呢?
(二)測(cè)試數(shù)據(jù)
經(jīng)過(guò)調(diào)研,在知名開(kāi)源軟件中,暫未發(fā)現(xiàn)直接采用io_uring進(jìn)行網(wǎng)絡(luò)IO的方案,如seastar/nginx等都沒(méi)有官方支持,既然可借鑒較少,那么就自行測(cè)試。
由于io_uring還處于完善階段,而且對(duì)于網(wǎng)絡(luò)IO的支持也有多種方式。目前我們梳理出其中3種:
Proactor:io_uring直接recv/send。
Reactor:io_uring接管socket_fd(POLL_ADD)后再recv/send。
io_uring接管epoll_fd后再epoll_wait再RECV/SEND:路徑繁瑣,推測(cè)性能不佳,直接略過(guò)。
為此,我們針對(duì)前兩種io_uring模型,以及常用的epoll模型,進(jìn)行測(cè)試對(duì)比。
為了利用更多的io_uring特性,測(cè)試采用當(dāng)前最新kernel(5.15)。測(cè)試模型如下:
通信協(xié)議:tcp echo
服務(wù)模型:?jiǎn)尉€程,異步并發(fā)
壓測(cè)客戶端:多線程,每個(gè)線程一個(gè)連接同步測(cè)試
數(shù)據(jù):包大小為512B
測(cè)試環(huán)境:本機(jī)通信loopback接口
epoll

io_uring(Proactor)

io_uring(Reactor)
目前網(wǎng)上的很多程序采用此方式。不過(guò)從理論上分析,應(yīng)該epoll性能接近,故暫未測(cè)試。
(三)數(shù)據(jù)分析
通過(guò)對(duì)比、分析以上的測(cè)試數(shù)據(jù),可以得到以下結(jié)論:
io_uring在網(wǎng)絡(luò)IO方面,并不比epoll性能強(qiáng)大。網(wǎng)絡(luò)IO的主要瓶頸還是在于內(nèi)核協(xié)議棧的開(kāi)銷。
io_uring即使開(kāi)啟內(nèi)核輪詢,在負(fù)載低時(shí)可降低延時(shí),而滿載性能提升不明顯,反而浪費(fèi)了CPU資源。
(四)業(yè)務(wù)落地
在Linux網(wǎng)絡(luò)IO場(chǎng)景中,io_uring并不比epoll帶來(lái)額外的性能提升。這與存儲(chǔ)IO不同。
不過(guò)值得思考的是,如果一個(gè)系統(tǒng)中同時(shí)存在網(wǎng)絡(luò)IO和存儲(chǔ)IO,對(duì)比以下兩種方式:
網(wǎng)絡(luò)IO采用epoll,存儲(chǔ)IO采用io_uring(可結(jié)合eventfd與epoll配合)
網(wǎng)絡(luò)IO、存儲(chǔ)IO都采用io_uring。
從理論上分析,方式2可以依賴io_uring批量提交等優(yōu)化,從而進(jìn)一步減少系統(tǒng)調(diào)用,是否可以帶來(lái)性能提升呢?
這部分需要進(jìn)一步測(cè)試分析。
四、總結(jié)
以上簡(jiǎn)單介紹了RDMA、io_uring/socket等方案,各有優(yōu)缺點(diǎn)以及場(chǎng)景限制。后續(xù)將介紹DPDK的方案,敬請(qǐng)期待。
?作者簡(jiǎn)介
王猛
騰訊后臺(tái)開(kāi)發(fā)工程師
騰訊后臺(tái)開(kāi)發(fā)工程師,目前負(fù)責(zé)公司C++后臺(tái)開(kāi)發(fā)框架的研發(fā),有豐富的高并發(fā)開(kāi)發(fā)運(yùn)營(yíng)、高性能調(diào)優(yōu)經(jīng)驗(yàn)。
?推薦閱讀
圖文結(jié)合!Redis延遲隊(duì)列g(shù)olang高效實(shí)踐
開(kāi)箱即用!深入淺出Prometheus監(jiān)控神器
C++17在業(yè)務(wù)代碼中最好用的10個(gè)特性!


