解鎖tRPC高效能密碼:網路方案簡介!

語言: CN / TW / HK
導語 | 本文介紹了部分高效能網路方案,包括RDMA、HARP、io_uring等。從技術原理、落地可行性等方面,簡要地做出分析,希望能對此方面感興趣的開發者提供一些經驗和幫助。

 

一、背景

業務中經常會有這樣的場景:

隨著網絡卡速率的提升(10G/25G/100G),以及部分業務對低延遲的極致追求(1ms/50us),目前的核心協議棧由於協議複雜、流程複雜、設計陳舊等因素,已經逐漸成為業務瓶頸。

業界已經有部分RDMA、DPDK的實踐,但是對於大多數開發者而言,依然比較陌生。

那麼這些方案各自的場景究竟怎樣?是否能夠為更多的業務賦能?以下是階段性簡要總結。

 

二、RDMA

 

(一)原理簡介

 

相對於傳統的網路協議棧,RDMA提供的關鍵特性即為:Kernel Bypass,也即利用專用的NIC(網絡卡)進行硬體層面的協議傳輸、編解碼(Offload),通過記憶體對映技術直接與使用者態程式互動,從而避免了複雜低效的核心中介。

基於這種設計,隨之提供幾個額外的重要特性:

  • Zero-Copy:基於DMA操作,通訊全程沒有額外的CPU介入拷貝,從而降低CPU消耗。
  • 穩定低延遲:由於硬體通路的可靠性,從而保證了穩定的通訊延遲。
  • 多種傳輸模式:RC、RD、UC、UD等。基於不同業務的不同可靠性和效能需求,提供類似TCP/UDP的多種傳輸模式。

 

 

由於RDMA定位為高效能網路傳輸,同時也為了簡化硬體的設計,一般來說,RDMA會避免如軟體TCP那樣複雜的可靠性設計,而是極其依賴底層傳輸網路的可靠性。

根據不同的傳輸網路,RDMA的具體實現分為幾類:

 

 

另外補充說明:

  • 雖然RoCE v1/2依賴融合融合乙太網,也即無損傳輸,不過也有部分廠商的優化實現,可以減輕對無損傳輸的依賴。
  • Linux kernel 4.9+中,實現了Soft-RoCE,也即軟體版本的RoCE v2,主要用於測試、學習。

 

(二)RoCE v2 v.s. iWARP

在乙太網環境,主要可選項為RoCE v2和iWARP,相關對比如下:

目前來看,目前的機房網路建設中,對RoCE v2的支援更好,而iWARP卻仍然處於相對空白的狀態。

為此,當前的調研主要針對RoCE v2,而iWARP仍然有待探索。

(三)業務落地

 

後臺業務主流協議仍然是TCP,具有執行穩定、除錯工具豐富等優勢。不過對於少數期望高效能的業務,RDMA也是值得考慮的。

 

業務使用RDMA主要面臨兩方面的困難:

  • RoCE v2無損網路的要求導致難以跨機房傳輸,當前騰訊機房的支援為module內傳輸(如5跳之內)。
  • 全新的開發介面如libverbs、UCX等,業務軟體需要進行適配。

而有些儲存業務依賴多副本,網路傳輸需要能夠跨越MAN,甚至跨城市傳輸。這直接導致RoCE v2難以落地。

 

三、io_uring/socket

(一)原理簡介

io_uring是Linux 5.1+中支援的非同步IO框架,其核心優勢有:

  • 真正的非同步化設計(Proactor),而非如epoll等本質上的同步行為(Reactor)。而其關鍵在於,程式和kernel通過SQ/CQ兩個佇列進行解耦。
  • 統一的非同步IO框架,不僅支援儲存、網路。由於良好的擴充套件性,甚至可以支援任何的系統呼叫,如openat、stat等。

如前述,一個io_uring的例項,會建立一對核心和使用者程式共享的佇列,也即提交佇列SQ和完成佇列CQ,兩者皆為SPSC範型:

  • SQ:使用者態執行緒生產,然後系統呼叫(io_uring_enter)通知核心(io_wq kernel thread)消費。其中元素稱為SQE。
  • CQ:核心生產,然後通知(若使用者程式睡眠等待則喚醒)使用者態消費。其中元素稱為CQE。

這其實是最常規也是最經典的非同步模型,在眾多非同步設計中可見。

一般情況下,CQE和SQE一一對應,不過io_uring支援multi-shot模式後則不一定如此。

另外,io_uring支援批量生產和消費,也即連續生產多個SQ後,一次性通知核心,或者持續消費CQ直到其空。

 

為了進一步優化部分場景的效能,io_uring支援眾多的高階特性:

  • File Registration:在反覆操作同一個fd時,加速其查詢對映。
  • Buffer Registration:在read/write等反覆需要在核心和使用者程式交換資料的場景,可以重複利用預註冊的一批記憶體。
  • Automatic Buffer Selection:為Proactor read預註冊一批記憶體,在就緒後核心自動選擇其中一塊存放資料,從而減少記憶體分配釋放,也節約記憶體資源。
  • SQ Polling:使核心(io_wq)輪詢SQ指定時間才睡眠,從而減少通知的系統呼叫。
  • IO Polling:開啟子系統(儲存、網路等)的輪詢模式(需要裝置驅動支援),從而加速部分高速裝置。另外可以配合io_uring_enter(flag:IORING_ENTER_GETEVENTS)進行忙等。
  • Multi-Shot:一次提交,多次完成,如只要一次提交socket accept,後續連線到來後多次返回。

io_uring在儲存IO場景,相對之前的阻塞IO、glibc aio、linux aio等,都有不錯的效能提升。

 

那麼在網路IO場景呢?是否優於epoll等方案呢?

 

(二)測試資料

 

經過調研,在知名開源軟體中,暫未發現直接採用io_uring進行網路IO的方案,如seastar/nginx等都沒有官方支援,既然可借鑑較少,那麼就自行測試。

由於io_uring還處於完善階段,而且對於網路IO的支援也有多種方式。目前我們梳理出其中3種:

  • Proactor:io_uring直接recv/send。
  • Reactor:io_uring接管socket_fd(POLL_ADD)後再recv/send。
  • io_uring接管epoll_fd後再epoll_wait再RECV/SEND:路徑繁瑣,推測效能不佳,直接略過。

為此,我們針對前兩種io_uring模型,以及常用的epoll模型,進行測試對比。

為了利用更多的io_uring特性,測試採用當前最新kernel(5.15)。測試模型如下:

  • 通訊協議:tcp echo
  • 服務模型:單執行緒,非同步併發
  • 壓測客戶端:多執行緒,每個執行緒一個連線同步測試
  • 資料:包大小為512B
  • 測試環境:本機通訊loopback介面

 

  • epoll

  • io_uring(Proactor)

  • io_uring(Reactor)

 

目前網上的很多程式採用此方式。不過從理論上分析,應該epoll效能接近,故暫未測試。

(三)資料分析

 

通過對比、分析以上的測試資料,可以得到以下結論:

  • io_uring在網路IO方面,並不比epoll效能強大。網路IO的主要瓶頸還是在於核心協議棧的開銷。
  • io_uring即使開啟核心輪詢,在負載低時可降低延時,而滿載效能提升不明顯,反而浪費了CPU資源。

(四)業務落地

 

在Linux網路IO場景中,io_uring並不比epoll帶來額外的效能提升。這與儲存IO不同。

不過值得思考的是,如果一個系統中同時存在網路IO和儲存IO,對比以下兩種方式:

  • 網路IO採用epoll,儲存IO採用io_uring(可結合eventfd與epoll配合)
  • 網路IO、儲存IO都採用io_uring。

 

從理論上分析,方式2可以依賴io_uring批量提交等優化,從而進一步減少系統呼叫,是否可以帶來效能提升呢?

 

這部分需要進一步測試分析。

 

四、總結

 

以上簡單介紹了RDMA、io_uring/socket等方案,各有優缺點以及場景限制。後續將介紹DPDK的方案,敬請期待。

 

作者簡介

quintonwang,騰訊後臺開發工程師。