面對疾風吧!io_uring 優化 nginx 實戰演練

CSDN資訊發表於2020-09-28

作者:玖一(雲巔論劍)

引言

io_uring是Linux核心在v5.1引入的一套非同步IO介面,隨著其迅速發展,現在的io_uring已經遠遠超過了純IO的範疇。從Linux v5.3版本開始,io_uring陸續新增了網路程式設計相關的API,對使用者提供sendmsg、recvmsg、accept、connect等介面的非同步支援,將io_uring的生態範圍擴大到了網路領域。

另外從Linux v5.7開始,io_uring對這些非同步介面提供FAST POLL機制,使用者無需再使用像select、event poll等多路複用機制來監聽檔案控制程式碼,只要把讀寫請求直接丟到io_uring的submit queue中並提交,當檔案控制程式碼不可讀寫時,核心會主動新增poll handler,當檔案控制程式碼可讀寫時主動呼叫poll handler再次下發讀寫請求,從而減少系統呼叫次數提高效能。

上一篇我們初探了 io_uring 用於網路的程式設計模型以及 echo server benchmark 下的效能表現,這篇文章我們將基於通用應用 nginx 實戰。

Nginx io_uring 程式碼優化

Nginx是一款輕量級的Web伺服器、反向代理伺服器,由於它的記憶體佔用少,啟動極快,高併發能力強,在網際網路專案中廣泛應用。

在這裡插入圖片描述

從架構上看,Nginx由一個master和多個worker程式組成,多個worker之間不需要加鎖,獨立處理與client的連線和網路請求。worker是一個單執行緒大迴圈,這與上一篇“你認為 io_uring 只適用於儲存 IO?大錯特錯!”文章中描述的 echo server 模型基本一致。

基於event poll的程式設計模型

event poll是Nginx在Linux下的預設事件模型。

[Snipaste_2020-06-12_14-24-34.png]

event poll事件模型把listen fd以及新建連線的sock fd都註冊進event poll中,當這些fd上有資料可讀時,等待在epoll_wait()的worker程式會被喚醒,呼叫相應的回撥函式進行處理,這裡的recv、writev請求都為同步請求。

基於io_uring的程式設計模型

前面提到,io_uring的FAST POLL機制允許資料在未ready的情況下就直接下發,不需要再把普通連線的fd註冊進event poll。另外這裡的讀寫請求通過io_uring非同步下發,處理流程大致如下:

在這裡插入圖片描述

事實上,accept()也可以採取FAFST POLL機制,無需等待listen_fd資料可讀就直接下發,以減少系統呼叫次數。但在除錯過程中發現這樣accept()失敗概率大大增加,而每次失敗的accept()都會帶來一次無效的sock記憶體申請和釋放,這個開銷較大,因此依然採用類似event poll的方式來偵聽listen fd。後續針對這塊可以做一些優化。

測試結果

測試環境

  • 測試機器
    CPU: Intel® Xeon® CPU E5-2682 v4 @ 2.50GHz 64邏輯核
    server cmdline新增:mitigation=on

  • nginx配置

user root;
http {
    access_log  off;
    server {
        access_log  off; // 關閉access log,否則會寫日誌,影響測試
        location / {
            return 200;  // 不讀本地檔案,直接返回200
        }
    }
}
  • benchmark
    使用輕量級HTTP效能測試工具wrk進行壓測。

  • 測試命令

長連線 wrk -c $connection -t $thread -d 120 $url
短連線 wrk -c $connection -t $thread -H "Connection: Close" -d 120 $url

測試結果

長連線

  • connection=1000,thread=200, 測試server上不同worker數目效能。

[image.png]

worker數目在8以下時,QPS有20%左右的提升。隨著worker數目增大,CPU不成為瓶頸,收益逐漸降低。

  • server單worker,測試client端不同連線數效能(thread取預設數2)。

[image.png]

[image.png]

可以看到單worker情況下,500個連線以上,QPS有20%以上的提升。從系統呼叫數目上看,io uring的系統呼叫數基本上在event poll的1/10以內。

短連線

  • connection=1000,thread=200, 測試server上不同worker數目效能。

[image.png]

短連線場景,io uring相對於event poll非但沒有提升,甚至在某些場景下有5%~10%的效能下降。究其原因,除了io uring框架本身帶來的開銷以外,還可能跟io uring程式設計模式下請求批量下發而帶來的延遲有關。

總結及下一步工作

從筆者目前的測試來看,io_uring在網路程式設計方面的優化更適合長連線場景,在長連線場景下最高有20%多的提升。短連線場景還有待優化,主要考慮以下兩方面:

  • io uring本身框架開銷的優化,當然這個優化對長連線同樣適用。
  • 針對短連線的優化,如針對accept()請求,先檢查是否有資料可讀,避免無效記憶體申請釋放;多個accept()一起下發等。

nginx 和 echo server 等優化實踐相關內容(包含原始碼),我們都已經在 OpenAnolis 社群高效能儲存 SIG 開源(openanolis.org)。也歡迎大家積極參與討論和貢獻,一起探索 io_uring 的高效能之路。

歡迎釘釘掃碼入群交流!
在這裡插入圖片描述

相關文章