如何提升 Linux 上 socket 性能


加速網絡應用程序的 4 種方法算法

使用 Sockets API,咱們能夠開發客戶機和服務器應用程序,它們能夠在本地網絡上進行通訊,也能夠經過 Internet 在全球範圍內進行通訊。與其餘 API 同樣,您能夠經過一些方法使用 Sockets API,從而提升 Socket 的性能,或者限制 Socket 的性能。本文探索了 4 種使用 Sockets API 來獲取應用程序的最大性能並對 GNU/Linux® 環境進行優化從而達到最好結果的方法。shell


在開發 socket 應用程序時,首要任務一般是確保可靠性並知足一些特定的需求。利用本文中給出的 4 個提示,您就能夠從頭開始爲實現最佳性能來設計並開發 socket 程序。本文內容包括對於 Sockets API 的使用、兩個能夠提升性能的 socket 選項以及 GNU/Linux 優化。瀏覽器

爲了可以開發性能卓越的應用程序,請遵循如下技巧:服務器

最小化報文傳輸的延時。網絡

最小化系統調用的負載。socket

爲 Bandwidth Delay Product 調節 TCP 窗口。tcp

動態優化 GNU/Linux TCP/IP 棧。函數

技巧 1. 最小化報文傳輸的延時工具


在經過 TCP socket 進行通訊時,數據都拆分紅了數據塊,這樣它們就能夠封裝到給定鏈接的 TCP payload(指 TCP 數據包中的有效負荷)中了。TCP payload 的大小取決於幾個因素(例如最大報文長度和路徑),可是這些因素在鏈接發起時都是已知的。爲了達到最好的性能,咱們的目標是使用盡量多的可用數據來填充每一個報文。當沒有足夠的數據來填充 payload 時(也稱爲最大報文段長度(maximum segment size) 或 MSS),TCP 就會採用 Nagle 算法自動將一些小的緩衝區鏈接到一個報文段中。這樣能夠經過最小化所發送的報文的數量來提升應用程序的效率,並減輕總體的網絡擁塞問題。性能

儘管 John Nagle 的算法能夠經過將這些數據鏈接成更大的報文來最小化所發送的報文的數量,可是有時您可能但願只發送一些較小的報文。一個簡單的例子是 telnet 程序,它讓用戶能夠與遠程系統進行交互,這一般都是經過一個 shell 來進行的。若是用戶被要求用發送報文以前輸入的字符來填充某個報文段,那麼這種方法就絕對不能知足咱們的須要。

另一個例子是 HTTP 協議。一般,客戶機瀏覽器會產生一個小請求(一條 HTTP 請求消息),而後 Web 服務器就會返回一個更大的響應(Web 頁面)。

解決方案


您應該考慮的第一件事情是 Nagle 算法知足一種需求。因爲這種算法對數據進行合併,試圖構成一個完整的 TCP 報文段,所以它會引入一些延時。可是這種算法能夠最小化在線路上發送的報文的數量,所以能夠最小化網絡擁塞的問題。

可是在須要最小化傳輸延時的狀況中,Sockets API 能夠提供一種解決方案。要禁用 Nagle 算法,您能夠設置 TCP_NODELAY socket 選項,如清單 1 所示。

清單 1. 爲 TCP socket 禁用 Nagle 算法


int sock, flag, ret;

/* Create new stream socket */

sock = socket( AF_INET, SOCK_STREAM, 0 );

/* Disable the Nagle (TCP No Delay) algorithm */

flag = 1;

ret = setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) );

if (ret == -1) {

  printf("Couldn't setsockopt(TCP_NODELAY)\n");

  exit(-1);

}

提示:使用 Samba 的實驗代表,在從 Microsoft® Windows® 服務器上的 Samba 驅動器上讀取數據時,禁用 Nagle 算法幾乎能夠加倍提升讀性能。


技巧 2. 最小化系統調用的負載


任什麼時候候經過一個 socket 來讀寫數據時,您都是在使用一個系統調用(system call)。這個調用(例如 read 或 write)跨越了用戶空間應用程序與內核的邊界。另外,在進入內核以前,您的調用會經過 C 庫來進入內核中的一個通用函數(system_call())。從 system_call()中,這個調用會進入文件系統層,內核會在這兒肯定正在處理的是哪一種類型的設備。最後,調用會進入 socket 層,數據就是在這裏進行讀取或進行排隊從而經過 socket 進行傳輸的(這涉及數據的副本)。

這個過程說明系統調用不只僅是在應用程序和內核中進行操做的,並且還要通過應用程序和內核中的不少層次。這個過程耗費的資源很高,所以調用次數越多,經過這個調用鏈進行的工做所須要的時間就越長,應用程序的性能也就越低。

因爲咱們沒法避免這些系統調用,所以唯一的選擇是最小化使用這些調用的次數。幸運的是,咱們能夠對這個過程進行控制。

解決方案


在將數據寫入一個 socket 時,儘可能一次寫入全部的數據,而不是執行屢次寫數據的操做。對於讀操做來講,最好傳入能夠支持的最大緩衝區,由於若是沒有足夠多的數據,內核也會試圖填充整個緩衝區(另外還須要保持 TCP 的通告窗口爲打開狀態)。這樣,您就能夠最小化調用的次數,並能夠實現更好的總體性能。


技巧 3. 爲 Bandwidth Delay Product 調節 TCP 窗口


TCP 的性能取決於幾個方面的因素。兩個最重要的因素是連接帶寬(link bandwidth)(報文在網絡上傳輸的速率)和 往返時間(round-trip time) 或 RTT(發送報文與接收到另外一端的響應之間的延時)。這兩個值肯定了稱爲 Bandwidth Delay Product(BDP)的內容。

給定連接帶寬和 RTT 以後,您就能夠計算出 BDP 的值了,不過這表明什麼意義呢?BDP 給出了一種簡單的方法來計算理論上最優的 TCP socket 緩衝區大小(其中保存了排隊等待傳輸和等待應用程序接收的數據)。若是緩衝區過小,那麼 TCP 窗口就不能徹底打開,這會對性能形成限制。若是緩衝區太大,那麼寶貴的內存資源就會形成浪費。若是您設置的緩衝區大小正好合適,那麼就能夠徹底利用可用的帶寬。下面咱們來看一個例子:

BDP = link_bandwidth * RTT

若是應用程序是經過一個 100Mbps 的局域網進行通訊,其 RRT 爲 50 ms,那麼 BDP 就是:

100MBps * 0.050 sec / 8 = 0.625MB = 625KB

注意:此處除以 8 是將位轉換成通訊使用的字節。

所以,咱們能夠將 TCP 窗口設置爲 BDP 或 1.25MB。可是在 Linux 2.6 上默認的 TCP 窗口大小是 110KB,這會將鏈接的帶寬限制爲 2.2MBps,計算方法以下:

throughput = window_size / RTT


110KB / 0.050 = 2.2MBps

若是使用上面計算的窗口大小,咱們獲得的帶寬就是 12.5MBps,計算方法以下:

625KB / 0.050 = 12.5MBps

差異的確很大,而且能夠爲 socket 提供更大的吞吐量。所以如今您就知道如何爲您的 socket 計算最優的緩衝區大小了。可是又該如何來改變呢?

解決方案


Sockets API 提供了幾個 socket 選項,其中兩個能夠用於修改 socket 的發送和接收緩衝區的大小。清單 2 展現瞭如何使用 SO_SNDBUF 和SO_RCVBUF 選項來調整發送和接收緩衝區的大小。

注意:儘管 socket 緩衝區的大小肯定了通告 TCP 窗口的大小,可是 TCP 還在通告窗口內維護了一個擁塞窗口。所以,因爲這個擁塞窗口的存在,給定的 socket 可能永遠都不會利用最大的通告窗口。

清單 2. 手動設置發送和接收 socket 緩衝區大小


int ret, sock, sock_buf_size;

sock = socket( AF_INET, SOCK_STREAM, 0 );

sock_buf_size = BDP;

ret = setsockopt( sock, SOL_SOCKET, SO_SNDBUF,

                   (char *)&sock_buf_size, sizeof(sock_buf_size) );

ret = setsockopt( sock, SOL_SOCKET, SO_RCVBUF,

                   (char *)&sock_buf_size, sizeof(sock_buf_size) );

在 Linux 2.6 內核中,發送緩衝區的大小是由調用用戶來定義的,可是接收緩衝區會自動加倍。您能夠進行 getsockopt 調用來驗證每一個緩衝區的大小。

巨幀(jumbo frame)


咱們還能夠考慮將包的大小從 1,500 字節修改成 9,000 字節(稱爲巨幀)。在本地網絡中能夠經過設置最大傳輸單元(Maximum Transmit Unit,MTU)來設置巨幀,這能夠極大地提升性能。

就 window scaling 來講,TCP 最初能夠支持最大爲 64KB 的窗口(使用 16 位的值來定義窗口的大小)。採用 window scaling(RFC 1323)擴展以後,您就可使用 32 位的值來表示窗口的大小了。GNU/Linux 中提供的 TCP/IP 棧能夠支持這個選項(以及其餘一些選項)。

提示:Linux 內核還包括了自動對這些 socket 緩衝區進行優化的能力(請參閱下面 表 1 中的tcp_rmem 和 tcp_wmem),不過這些選項會對整個棧形成影響。若是您只須要爲一個鏈接或一類鏈接調節窗口的大小,那麼這種機制也許不能知足您的須要了。

技巧 4. 動態優化 GNU/Linux TCP/IP 棧


標準的 GNU/Linux 發行版試圖對各類部署狀況都進行優化。這意味着標準的發行版可能並無對您的環境進行特殊的優化。

解決方案


GNU/Linux 提供了不少可調節的內核參數,您可使用這些參數爲您本身的用途對操做系統進行動態配置。下面咱們來了解一下影響 socket 性能的一些更重要的選項。

在 /proc 虛擬文件系統中存在一些可調節的內核參數。這個文件系統中的每一個文件都表示一個或多個參數,它們能夠經過 cat 工具進行讀取,或使用 echo 命令進行修改。清單 3 展現瞭如何查詢或啓用一個可調節的參數(在這種狀況中,能夠在 TCP/IP 棧中啓用 IP 轉發)。

清單 3. 調優:在 TCP/IP 棧中啓用 IP 轉發


[root@camus]# cat /proc/sys/net/ipv4/ip_forward

0

[root@camus]# echo "1" > /poc/sys/net/ipv4/ip_forward

[root@camus]# cat /proc/sys/net/ipv4/ip_forward

1

[root@camus]#

表 1 給出了幾個可調節的參數,它們能夠幫助您提升 Linux TCP/IP 棧的性能。

表 1. TCP/IP 棧性能使用的可調節內核參數

可調節的參數 默認值 選項說明

/proc/sys/net/core/rmem_default "110592" 定義默認的接收窗口大小;對於更大的 BDP 來講,這個大小也應該更大。

/proc/sys/net/core/rmem_max "110592" 定義接收窗口的最大大小;對於更大的 BDP 來講,這個大小也應該更大。

/proc/sys/net/core/wmem_default "110592" 定義默認的發送窗口大小;對於更大的 BDP 來講,這個大小也應該更大。

/proc/sys/net/core/wmem_max "110592" 定義發送窗口的最大大小;對於更大的 BDP 來講,這個大小也應該更大。

/proc/sys/net/ipv4/tcp_window_scaling "1" 啓用 RFC 1323 定義的 window scaling;要支持超過 64KB 的窗口,必須啓用該值。

/proc/sys/net/ipv4/tcp_sack "1" 啓用有選擇的應答(Selective Acknowledgment),這能夠經過有選擇地應答亂序接收到的報文來提升性能(這樣可讓發送者只發送丟失的報文段);(對於廣域網通訊來講)這個選項應該啓用,可是這會增長對 CPU 的佔用。

/proc/sys/net/ipv4/tcp_fack "1" 啓用轉發應答(Forward Acknowledgment),這能夠進行有選擇應答(SACK)從而減小擁塞狀況的發生;這個選項也應該啓用。

/proc/sys/net/ipv4/tcp_timestamps "1" 以一種比重發超時更精確的方法(請參閱 RFC 1323)來啓用對 RTT 的計算;爲了實現更好的性能應該啓用這個選項。

/proc/sys/net/ipv4/tcp_mem "24576 32768 49152" 肯定 TCP 棧應該如何反映內存使用;每一個值的單位都是內存頁(一般是 4KB)。第一個值是內存使用的下限。第二個值是內存壓力模式開始對緩衝區使用應用壓力的上限。第三個值是內存上限。在這個層次上能夠將報文丟棄,從而減小對內存的使用。對於較大的 BDP 能夠增大這些值(可是要記住,其單位是內存頁,而不是字節)。

/proc/sys/net/ipv4/tcp_wmem "4096 16384 131072" 爲自動調優定義每一個 socket 使用的內存。第一個值是爲 socket 的發送緩衝區分配的最少字節數。第二個值是默認值(該值會被 wmem_default 覆蓋),緩衝區在系統負載不重的狀況下能夠增加到這個值。第三個值是發送緩衝區空間的最大字節數(該值會被 wmem_max 覆蓋)。

/proc/sys/net/ipv4/tcp_rmem "4096 87380 174760" 與 tcp_wmem 相似,不過它表示的是爲自動調優所使用的接收緩衝區的值。

/proc/sys/net/ipv4/tcp_low_latency "0" 容許 TCP/IP 棧適應在高吞吐量狀況下低延時的狀況;這個選項應該禁用。

/proc/sys/net/ipv4/tcp_westwood "0" 啓用發送者端的擁塞控制算法,它能夠維護對吞吐量的評估,並試圖對帶寬的總體利用狀況進行優化;對於 WAN 通訊來講應該啓用這個選項。

/proc/sys/net/ipv4/tcp_bic "1" 爲快速長距離網絡啓用 Binary Increase Congestion;這樣能夠更好地利用以 GB 速度進行操做的連接;對於 WAN 通訊應該啓用這個選項。

與任何調優努力同樣,最好的方法實際上就是不斷進行實驗。您的應用程序的行爲、處理器的速度以及可用內存的多少都會影響到這些參數影響性能的方式。在某些狀況中,您認爲有益的操做可能偏偏是有害的(反之亦然)。所以,咱們須要逐一試驗各個選項,而後檢查每一個選項的結果。換而言之,咱們須要相信本身的經驗,可是對每次修改都要進行驗證。

提示:下面介紹一個有關永久性配置的問題。注意,若是您從新啓動了 GNU/Linux 系統,那麼您所須要的任何可調節的內核參數都會恢復成默認值。爲了將您所設置的值做爲這些參數的默認值,可使用 /etc/sysctl.conf 在系統啓動時將這些參數配置成您所設置的值。

GNU/Linux 工具


GNU/Linux 對我很是有吸引力,這是由於其中有不少工具可使用。儘管其中大部分都是命令行工具,可是它們都很是有用,並且很是直觀。GNU/Linux 提供了幾個工具 —— 有些是 GNU/Linux 本身提供的,有些是開放源碼軟件 —— 用於調試網絡應用程序,測量帶寬/吞吐量,以及檢查連接的使用狀況。

表 2 列出最有用的幾個 GNU/Linux 工具,以及它們的用途。表 3 列出了 GNU/Linux 發行版沒有提供的幾個有用工具。有關表 3 中工具的更多信息請參閱 參考資料。

表 2. 任何 GNU/Linux 發行版中均可以找到的工具

GNU/Linux 工具 用途

ping 這是用於檢查主機的可用性的最經常使用的工具,可是也能夠用於識別帶寬延時產品計算的 RTT。

traceroute 打印某個鏈接到網絡主機所通過的包括一系列路由器和網關的路徑(路由),從而肯定每一個 hop 之間的延時。

netstat 肯定有關網絡子系統、協議和鏈接的各類統計信息。

tcpdump 顯示一個或多個鏈接的協議級的報文跟蹤信息;其中還包括時間信息,您可使用這些信息來研究不一樣協議服務的報文時間。

表 3. GNU/Linux 發行版中沒有提供的有用性能工具

GNU/Linux 工具 用途

netlog 爲應用程序提供一些有關網絡性能方面的信息。

nettimer 爲瓶頸連接帶寬生成一個度量標準;能夠用於協議的自動優化。

Ethereal 以一個易於使用的圖形化界面提供了 tcpump(報文跟蹤)的特性。

iperf 測量 TCP 和 UDP 的網絡性能;測量最大帶寬,並彙報延時和數據報的丟失狀況。

結束語

嘗試使用本文中介紹的技巧和技術來提升 socket 應用程序的性能,包括經過禁用 Nagle 算法來減小傳輸延時,經過設置緩衝區的大小來提升 socket 帶寬的利用,經過最小化系統調用的個數來下降系統調用的負載,以及使用可調節的內核參數來優化 Linux 的 TCP/IP 棧。

在進行優化時還須要考慮應用程序的特性。例如,您的應用程序是基於 LAN 的仍是會經過 Internet 進行通訊?若是您的應用程序僅僅會在 LAN 內部進行操做,那麼增大 socket 緩衝區的大小可能不會帶來太大的改進,不過啓用巨幀卻必定會極大地改進性能!

最後,還要使用 tcpdump 或 Ethereal 來檢查優化以後的結果。在報文級看到的變化能夠幫助展現使用這些技術進行優化以後所取得的成功效果。

相關文章
相關標籤/搜索