問題現象
在進行網路效能測試或高並發業務運行期間,發現網路收發效能未達預期(如輸送量受限、延遲增加)。
通過檢查netstat -s命令的輸出,發現 "packet pruned from receive queue because of socket buffer overrun" :
$ netstat -s | grep "socket buffer overrun"
1617 packets pruned from receive queue because of socket buffer overrun問題原因
直接原因
當網路包到達的速度超過了應用程式從通訊端接收緩衝區(Receive Buffer)讀取資料的速度時,接收隊列(Receive Queue)會逐漸填滿。
核心行為
當前接收隊列長度 + 當前收包大小 > 接收隊列最大長度:
記憶體規整(Pruning/Collapsing): Linux 核心會嘗試對接收隊列進行記憶體規整(例如合并小包、釋放亂序包的中繼資料結構),以騰出空間接收新包。
效能影響: 規整操作消耗CPU資源,且如果規整後仍無空間,資料包將被丟棄,導致 TCP 重傳,進而導致網路延遲增加和輸送量下降。
根本原因
當前系統的通訊端接收緩衝區配置(核心參數或應用設定)無法滿足當前業務流量的突發需求或輸送量需求。
解決方案
情境一:應用程式使用系統預設緩衝區
如果應用程式沒有顯式調用setsockopt(SO_RCVBUF) 設定緩衝區大小,Linux核心會根據tcp_rmem參數自動調節TCP接收視窗大小(Auto-tuning)。
調整方法:
調整 /proc/sys/net/ipv4/tcp_rmem 中的三個值(單位:位元組)。這三個值分別代表:min(最小)、default(預設)、max(最大)。
查看當前值。
sysctl net.ipv4.tcp_rmem # 輸出樣本: 4096 87380 4194304 (分別為 4KB, 87KB, 4MB)適當增大max值(第三個數字),以允許核心在記憶體充足時分配更大的接收緩衝區。
# 樣本:將最大值調整為16MB (具體數值請根據伺服器記憶體和業務需求設定) sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"持久化配置(寫入/etc/sysctl.conf)。
echo "net.ipv4.tcp_rmem = 4096 87380 16777216" >> /etc/sysctl.conf sysctl -p
情境二:應用程式手動設定了緩衝區 (setsockopt)
如果應用程式代碼中調用了setsockopt(socket, SOL_SOCKET, SO_RCVBUF, ...),則核心的自動調節機制(Auto-tuning)會被禁用,緩衝區大小將被固定。
方法一(推薦):修改應用代碼
移除代碼中的 setsockopt(SO_RCVBUF) 調用,讓核心接管緩衝區管理,從而利用核心高效的自動調節機制。方法二:增大應用設定的值 & 調整核心上限
如果應用程式必須手動設定,需在代碼中增大設定的值。應用程式通過setsockopt設定的最大值受到核心參數net.core.rmem_max的限制。例如:應用申請1MB,但rmem_max只有256KB,實際只會分配256KB。因此,必須同步調整核心上限:# 查看當前上限 sysctl net.core.rmem_max # 調大上限 (例如調整為 16MB) sysctl -w net.core.rmem_max=16777216
驗證修複
調整配置並重啟應用程式(或重建立立串連)後,重新進行壓力測試,並監控指標:
檢查計數器是否停止增長:
watch -d "netstat -s | grep 'socket buffer overrun'"如果數值不再增加或增加頻率大幅降低,說明調整有效。
觀察業務效能:
確認網路輸送量是否提升,延遲是否平穩。
注意事項
估算記憶體消耗:TCP接收緩衝區使用的是系統實體記憶體(非swap)。如果並發串連數非常多(如數萬個),將緩衝區上限設定得過大可能導致系統耗盡記憶體,觸發OOM Killer殺掉業務進程。
記憶體消耗估算:串連數 × 平均緩衝區大小。
應用讀取開銷:雖然較大的緩衝區可以減少丟包,但如果緩衝區遠大於應用程式一次能讀取的資料量,可能會造成記憶體浪費,且對快取命中率有一定影響(儘管相比丟包,這個影響通常次要)。
生效範圍
net.ipv4.tcp_rmem僅影響TCP通訊端。
net.core.rmem_default和net.core.rmem_max影響所有類型的通訊端(包括 UDP、Unix Domain Socket 等)。