一、上節回顧
上一節,我們了解了 NAT(網絡地址轉換)的原理,學會了如何排查 NAT 帶來的性能問題,最后還總結了 NAT 性能優化的基本思路。我先帶你簡單回顧一下。
NAT 基於 Linux 內核的連接跟蹤機制,實現了 IP 地址及端口號重寫的功能,主要被用來解決公網 IP 地址短缺的問題。
在分析 NAT 性能問題時,可以先從內核連接跟蹤模塊 conntrack 角度來分析,比如用systemtap、perf、netstat 等工具,以及 proc 文件系統中的內核選項,來分析網絡協議
棧的行為;然后,通過內核選項調優、切換到無狀態 NAT、使用 DPDK 等方式,進行實際優化。
通過前面的學習,你應該已經體會到,網絡問題比我們前面學過的 CPU、內存或磁盤 I/O都要復雜。無論是應用層的各種 I/O 模型,冗長的網絡協議棧和眾多的內核選項,抑或是
各種復雜的網絡環境,都提高了網絡的復雜性。
不過,也不要過分擔心,只要你掌握了 Linux 網絡的基本原理和常見網絡協議的工作流程,再結合各個網絡層的性能指標來分析,你會發現,定位網絡瓶頸並不難。
找到網絡性能瓶頸后,下一步要做的就是優化了,也就是如何降低網絡延遲,並提高網絡的吞吐量。學完相關原理和案例后,我就來講講,優化網絡性能問題的思路和一些注意事項。
由於網絡優化思路的內容比較多,我們分兩節來學習,今天我們先來看上篇。
二、確定優化目標
跟 CPU 和 I/O 方面的性能優化一樣,優化前,我會先問問自己,網絡性能優化的目標是什么?換句話說,我們觀察到的網絡性能指標,要達到多少才合適呢?
實際上,雖然網絡性能優化的整體目標,是降低網絡延遲(如 RTT)和提高吞吐量(如BPS 和 PPS),但具體到不同應用中,每個指標的優化標准可能會不同,優先級順序也大相徑庭。
就拿上一節提到的 NAT 網關來說,由於其直接影響整個數據中心的網絡出入性能,所以NAT 網關通常需要達到或接近線性轉發,也就是說, PPS 是最主要的性能目標。
再如,對於數據庫、緩存等系統,快速完成網絡收發,即低延遲,是主要的性能目標。而對於我們經常訪問的 Web 服務來說,則需要同時兼顧吞吐量和延遲。
所以,為了更客觀合理地評估優化效果,我們首先應該明確優化的標准,即要對系統和應用程序進行基准測試,得到網絡協議棧各層的基准性能。
在 怎么評估系統的網絡性能 中,我已經介紹過,網絡性能測試的方法。簡單回顧一下,Linux 網絡協議棧,是我們需要掌握的核心原理。它是基於 TCP/IP 協議族的分層結構,我
用一張圖來表示這個結構。
明白了這一點,在進行基准測試時,我們就可以按照協議棧的每一層來測試。由於底層是其上方各層的基礎,底層性能也就決定了高層性能。所以我們要清楚,底層性能指標,其
實就是對應高層的極限性能。我們從下到上來理解這一點。
首先是網絡接口層和網絡層,它們主要負責網絡包的封裝、尋址、路由,以及發送和接收。每秒可處理的網絡包數 PPS,就是它們最重要的性能指標(特別是在小包的情況
下)。你可以用內核自帶的發包工具 pktgen ,來測試 PPS 的性能
再向上到傳輸層的 TCP 和 UDP,它們主要負責網絡傳輸。對它們而言,吞吐量(BPS)、連接數以及延遲,就是最重要的性能指標。你可以用 iperf 或 netperf ,來測試傳輸層的性能。
不過要注意,網絡包的大小,會直接影響這些指標的值。所以,通常,你需要測試一系列不同大小網絡包的性能。
最后,再往上到了應用層,最需要關注的是吞吐量(BPS)、每秒請求數以及延遲等指標。你可以用 wrk、ab 等工具,來測試應用程序的性能。
不過,這里要注意的是,測試場景要盡量模擬生產環境,這樣的測試才更有價值。比如,你可以到生產環境中,錄制實際的請求情況,再到測試中回放。
總之,根據這些基准指標,再結合已經觀察到的性能瓶頸,我們就可以明確性能優化的目標。
三、網絡性能工具
同前面學習一樣,我建議從指標和工具兩個不同維度出發,整理記憶網絡相關的性能工具。
第一個維度,從網絡性能指標出發,你更容易把性能工具同系統工作原理關聯起來,對性能問題有宏觀的認識和把握。這樣,當你想查看某個性能指標時,就能清楚知道,可以用
哪些工具。
這里,我把提供網絡性能指標的工具,做成了一個表格,方便你梳理關系和理解記憶。你可以把它保存並打印出來,隨時查看。當然,你也可以把它當成一個“指標工具”指南來使用。
再來看第二個維度,從性能工具出發。這可以讓你更快上手使用工具,迅速找出想要觀察的性能指標。特別是在工具有限的情況下,我們更要充分利用好手頭的每一個工具,用少
量工具也要盡力挖掘出大量信息。
同樣的,我也將這些常用工具,匯總成了一個表格,方便你區分和理解。自然,你也可以當成一個“工具指標”指南使用,需要時查表即可。
四、網絡性能優化
總的來說,先要獲得網絡基准測試報告,然后通過相關性能工具,定位出網絡性能瓶頸。再接下來的優化工作,就是水到渠成的事情了。
當然,還是那句話,要優化網絡性能,肯定離不開 Linux 系統的網絡協議棧和網絡收發流程的輔助。你可以結合下面這張圖再回憶一下這部分的知識。
接下來,我們就可以從應用程序、套接字、傳輸層、網絡層以及鏈路層等幾個角度,分別來看網絡性能優化的基本思路
五、應用程序
應用程序,通常通過套接字接口進行網絡操作。由於網絡收發通常比較耗時,所以應用程序的優化,主要就是對網絡 I/O 和進程自身的工作模型的優化。
相關內容,其實我們在 C10K 和 C1000K 回顧 的文章中已經學過了。這里我們簡單回顧一下。
1、從網絡 I/O 的角度來說,主要有下面兩種優化思路。
第一種是最常用的 I/O 多路復用技術 epoll,主要用來取代 select 和 poll。這其實是解決C10K 問題的關鍵,也是目前很多網絡應用默認使用的機制。
第二種是使用異步 I/O(Asynchronous I/O,AIO)。AIO 允許應用程序同時發起很多I/O 操作,而不用等待這些操作完成。等到 I/O 完成后,系統會用事件通知的方式,告訴
應用程序結果。不過,AIO 的使用比較復雜,你需要小心處理很多邊緣情況。
2、而從進程的工作模型來說,也有兩種不同的模型用來優化。
第一種,主進程 + 多個 worker 子進程。其中,主進程負責管理網絡連接,而子進程負責實際的業務處理。這也是最常用的一種模型。
第二種,監聽到相同端口的多進程模型。在這種模型下,所有進程都會監聽相同接口,並且開啟 SO_REUSEPORT 選項,由內核負責,把請求負載均衡到這些監聽進程中去。
除了網絡 I/O 和進程的工作模型外,應用層的網絡協議優化,也是至關重要的一點。我總結了常見的幾種優化方法。
- 使用長連接取代短連接,可以顯著降低 TCP 建立連接的成本。在每秒請求次數較多時,這樣做的效果非常明顯。
- 使用內存等方式,來緩存不常變化的數據,可以降低網絡 I/O 次數,同時加快應用程序的響應速度。
- 使用 Protocol Buffer 等序列化的方式,壓縮網絡 I/O 的數據量,可以提高應用程序的吞吐。
- 使用 DNS 緩存、預取、HTTPDNS 等方式,減少 DNS 解析的延遲,也可以提升網絡I/O 的整體速度。
六、套接字
套接字可以屏蔽掉 Linux 內核中不同協議的差異,為應用程序提供統一的訪問接口。每個套接字,都有一個讀寫緩沖區。
- 讀緩沖區,緩存了遠端發過來的數據。如果讀緩沖區已滿,就不能再接收新的數據。
- 寫緩沖區,緩存了要發出去的數據。如果寫緩沖區已滿,應用程序的寫操作就會被阻塞。
所以,為了提高網絡的吞吐量,你通常需要調整這些緩沖區的大小。比如:
增大每個套接字的緩沖區大小 net.core.optmem_max; 增大套接字接收緩沖區大小 net.core.rmem_max 和發送緩沖區大小net.core.wmem_max; 增大 TCP 接收緩沖區大小 net.ipv4.tcp_rmem 和發送緩沖區大小net.ipv4.tcp_wmem。
至於套接字的內核選項,我把它們整理成了一個表格,方便你在需要時參考:
不過有幾點需要你注意。
- tcp_rmem 和 tcp_wmem 的三個數值分別是 min,default,max,系統會根據這些設置,自動調整 TCP 接收 / 發送緩沖區的大小。
- udp_mem 的三個數值分別是 min,pressure,max,系統會根據這些設置,自動調整UDP 發送緩沖區的大小。
當然,表格中的數值只提供參考價值,具體應該設置多少,還需要你根據實際的網絡狀況來確定。比如,發送緩沖區大小,理想數值是吞吐量 * 延遲,這樣才可以達到最大網絡利用率。
除此之外,套接字接口還提供了一些配置選項,用來修改網絡連接的行為。
- 為 TCP 連接設置 TCP_NODELAY 后,就可以禁用 Nagle 算法;
- 為 TCP 連接開啟 TCP_CORK 后,可以讓小包聚合成大包后再發送(注意會阻塞小包的發送);
- 使用 SO_SNDBUF 和 SO_RCVBUF ,可以分別調整套接字發送緩沖區和接收緩沖區的大小。
七、小結
今天,我們一起梳理了常見的 Linux 網絡性能優化方法
在優化網絡性能時,你可以結合 Linux 系統的網絡協議棧和網絡收發流程,然后從應用程序、套接字、傳輸層、網絡層再到鏈路層等,進行逐層優化。
當然,其實我們分析、定位網絡瓶頸,也是基於這些進行的。定位出性能瓶頸后,就可以根據瓶頸所在的協議層進行優化。比如,今天我們學了應用程序和套接字的優化思路:
在應用程序中,主要優化 I/O 模型、工作模型以及應用層的網絡協議; 在套接字層中,主要優化套接字的緩沖區大小。
而其他各個網絡層的優化方法,建議你先自己想一想,下一節,我們再來一起總結。