目錄
零、基本說明
一、內核參數
二、相關機制
1、GRO
(1)適用場景
(2)優缺點
(3)相關操作
2、Nagle 算法
(1)基本規則
(2)優缺點
(3)相關操作
3、Socket IO 復用?
零、基本說明
? ? ? ? 本篇博客會持續更新網絡編程中經常遇到的內核參數調整和相關網絡功能優化機制,一切以提高服務器性能為目的。具體參數調整,根據業務需求來。
? ? ? ? 若讀者在日常工作中遇到了相關的問題,也可以提出來一起討論學習。
一、內核參數
? ? ? ? 由于默認的 Linux 內核參數考慮的是最通用的場景,這明顯不符合用于支持高并發訪問的網絡服務器的定義,所以需要修改 Linux 內核參數,使得網絡服務器擁有更高的性能。
? ? ? ? 在優化內核時,可以做的事情很多,不過,我們通常會根據業務特點來進行調整,下面是只針對最通用的、使 TCP 服務器支持更多并發請求的網絡參數做簡單說明。
# 設置系統全局可以打開的最大文件描述符數量,ulimit -a 可以查看用戶級文件描述符數量的限制
fs.file-max = 6815744 # 允許重用處于 TIME_WAIT 狀態的套接字,用于處理高并發連接(系統中總會存在大量的 TIME_WAIT 狀態的連接)。
net.ipv4.tcp_tw_reuse = 1# 設置 TCP 連接在沒有數據傳輸時保持活動狀態的時間(秒),設置小些可以快速清理掉無效連接
net.ipv4.tcp_keepalive_time = 600# 設置 TCP 連接在關閉時等待 FIN 包(FIN-WAIT-2)確認的時間(秒)。
net.ipv4.tcp_fin_timeout = 30# 設置 TIME_WAIT 狀態的套接字最大數量。如果超過這個數字,TIMEWAIT 套接字將立刻被清除并打印警告信息。該參數默認為 180000,過多的 TIME WAIT套接字會使 Web 服務器變慢。
net.ipv4.tcp_max_tw_buckets = 5000# 設置本地端口范圍,控制服務器在發起連接時所使用的臨時源端口的范圍
net.ipv4.ip_local_port_range = 1024 61000# 設置 TCP 接收緩沖區的大小(字節),分別為最小值、默認值和最大值。
net.ipv4.tcp_rmem = 4096 32768 262142
# 設置 TCP 發送緩沖區的大小(字節),分別為最小值、默認值和最大值。
net.ipv4.tcp_wmem = 4096 32768 262142# 設置網絡設備的最大積壓隊列長度。當網卡接收數據包的速度大于內核處理的速度時,會有一個隊列保存這些數據包。這個參數表示該隊列的最大值。
net.core.netdev_max_backlog = 8096'''
注意:滑動窗口的大小與套接字緩存區會在一定程度上影響并發連接的數目。每個 TCP 連接都會為維護 TCP 滑動窗口而消耗內存,這個窗口會根據服務器的處理速度收縮或擴張。以 Nginx 為例,參數 wmem_max 的設置,需要平衡物理內存的總大小和Nginx 并發處理的最大連接數量(由 nginx.conf 中的 worker processes 和 worker connections 參數決定)而確定。當然如果僅僅為了提高并發量使服務器不出現 Out Of Memory 問題而去降低滑動窗口大小,那么并不合適,因為滑動窗口過小會影響大數據量的傳輸速度。rmem_default、wmem_default、rmem_max、wmem_max這4 個參數的設置需要根據我們的業務特性以及實際的硬件成本來綜合考慮。
'''
# 設置默認的接收緩沖區大小(字節)。
net.core.rmem_default = 262144
# 設置默認的發送緩沖區大小(字節)。
net.core.wmem_default = 262144
# 設置接收緩沖區的最大值(字節)。
net.core.rmem_max = 2097152
# 設置發送緩沖區的最大值(字節)。
net.core.wmem_max = 2097152# 該參數與性能無關,啟用 TCP SYN Cookies,用于防止 SYN 泛洪攻擊。
net.ipv4.tcp_syncookies = 1# 設置 TCP SYN 請求隊列的最大長度,將其設置得大一些,當服務器繁忙來不及 accept 新連接的情況時,Linux 不至于丟失客戶端發起的連接請求
net.ipv4.tcp_max_syn_backlog = 1024
二、相關機制
1、GRO
? GRO
是一種全球單播(Unicast)數據包接收優化技術。它允許多個數據包在一次中斷中被處理,從而減少 CPU 的利用率和提高數據傳輸效率。當網絡接口接收到多個數據包時,GRO
會將這些數據包聚合在一起,然后一次性觸發一個中斷來處理這些數據包。這樣,就減少了中斷的次數,提高了系統的整體性能。
(1)適用場景
? GRO
適用于高流量、低延遲的網絡環境,如數據中心、云計算平臺等。在這些環境中,GRO
可以顯著減少 CPU 的負載,提高網絡吞吐量。
(2)優缺點
? ? ? ? 優點:可提高網絡性能,通過減少中斷次數,GRO
可以顯著提高網絡吞吐量和降低 CPU 的利用率。同時由于減少了中斷次數,還降低了系統的能耗。
? ? ? ? 缺點:某些情況下,GRO
可能會導致數據包的處理延遲增加,因為它需要等待更多的數據包到達后才能觸發中斷。在一些對實時性要求較高的應用中,可能需要關閉GRO以確保數據的及時處理。
? ? ? ? 在配置?GRO
功能時,需要根據具體的應用場景和需求進行權衡。如果系統對實時性要求較高,或者網絡流量較小,那么關閉GRO
可能是一個更好的選擇。反之,如果系統需要處理大量的網絡流量,并且對延遲的要求不是特別高,那么開啟GRO
將有助于提高系統的整體性能。
(3)相關操作
# 關閉網卡 ens33 的 gro 功能
ethtool -K ens33 gro off
# 開啟網卡 ens33 的 gro 功能
ethtool -K ens33 gro on
2、Nagle 算法
? Nagle
算法是 TCP/IP 協議中的一種優化機制,主要目的是減少網絡中小數據包的數量,從而降低網絡擁塞,提高網絡帶寬的利用率。
(1)基本規則
? ? ? ? 在任意時刻,最多只能有一個未被確認的小段。小段為小于 MSS(最大分段大小)的數據塊,未被確認是指數據發出去后未收到對端的 ACK。
? ? ? ? 如果要發送的數據量達到了 MSS,則立即發送;
? ? ? ? 如果數據量小于 MSS 但之前所有發出去的包都已經收到了確認(ACK),也立即發送;
? ? ? ? 如果數據量小于 MSS 并且還有未被確認的包,則將數據緩存起來,當收到之前未被確認包的 ACK 或緩存的數據達到 MSS 時再將緩存的數據發送出去。
(2)優缺點
優點:
- 減少了網絡中的小包數量,降低了網絡負載;
- 減少了網絡擁塞的可能性、提高了網絡帶寬的利用率;
- 多個小包合成一個大包發送,減少了協議頭的開銷;
缺點:
- 增加了發送延遲,特別是對于需要快速響應的應用(如遠程終端操作);
- 很可能導致TCP粘包問題,使接收方應用難以分辨消息邊界。
? ? ? ? 在Linux系統中,Nagle 算法默認是啟用的。對于大多數應用,默認啟用Nagle算法是有益的。在實現應用層協議時,需要考慮Nagle算法可能帶來的影響,特別是在處理小數據包時。對于需要低延遲的應用(如在線游戲、遠程桌面等),可能需要禁用Nagle算法。
(3)相關操作
? ? ? ? 在Linux系統中,Nagle 算法默認是啟用的。使用 TCP_NODELAY 選項可以禁止 Nagle 算法。此時,應用程序向內核遞交的每個數據包都會立即發送出去。
適用 Nagle 時需要注意:
- Nagle 算法與 TCP 的 Delayed ACK 機制確認結合使用時,可能會導致性能問題。Delayed ACK 會推遲發送 ACK,而 Nagle 算法又在等待 ACK,這可能會導致不必要的延遲。
- Nagle 算法和 CORK 算法非常類似,但是它們的著眼點不一樣。Nagle 算法主要避免網絡因為太多的小包而擁塞,而 CORK 算法則是為了提高網絡的利用率。
? ? ? ? 下面 Demo 創建了一個 TCP 客戶端,使用 C 語言創建一個 TCP 套接字,連接到服務器,并禁用 Nagle 算法以減少延遲。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>int main() {int sockfd;struct sockaddr_in server_addr;char *message = "Hello, Server!";int flag = 1;// 創建套接字if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}// 設置服務器地址server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8090);inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);// 連接服務器if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("connection failed");close(sockfd);exit(EXIT_FAILURE);}// 禁用 Nagle 算法if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) < 0) {perror("setsockopt failed");close(sockfd);exit(EXIT_FAILURE);}// 發送數據send(sockfd, message, strlen(message), 0);printf("Message sent\n");// 關閉套接字close(sockfd);return 0;
}
3、Socket IO 復用?
? ? ? ?Socket IO 復用是網絡編程中用于處理多個客戶端連接的技術。其目的是在單線程或少量線程下,通過高效的機制來管理和響應多個并發的 I/O 請求,從而避免因頻繁創建和銷毀線程所帶來的資源消耗和性能瓶頸。
????????主要的技術有 select、poll 和 epoll,其中需要特別關注 epoll(Nginx 在 linux 下就是基于 epoll 實現高并發網絡連接)。epoll 基于事件驅動,通過紅黑樹高效管理文件描述符,避免了不必要的遍歷,使得時間復雜度降低到 O(1)。此外,epoll 還支持邊緣觸發和水平觸發兩種模式,提供了更高的靈活性和性能。
????????具體參考如下連接:
Linux下的三種 IO 復用_linux的io復用-CSDN博客https://blog.csdn.net/qq_37437983/article/details/144174475?spm=1001.2014.3001.5501