圖解 TCP 收發包需要注意的內核參數

背景

    TCP 收包和發包的過程是網絡應用中容易出現問題的地方。收包是指數據到達網卡並被應用程序開始處理的過程;發包則是應用程序調用發包函數到數據包從網卡發出的過程。常見的問題包括:

    要解決這些問題,我們需要理解 TCP 收發包過程中的關鍵因素,以及如何配置參數使之與業務場景匹配。下面我們來分析 TCP 數據包的發送和接收過程。

TCP 數據包的發送過程

摘自:https://time.geekbang.org/column/article/285816

    在應用程序調用 write(2) 或 send(2) 系列系統調用開始發包時,數據包會從用戶緩衝區複製到 TCP 發送緩衝區(TCP Send Buffer)。緩衝區大小默認由 net.ipv4.tcp_wmem 控制:

sysctl net.ipv4.tcp_wmem
net.ipv4.tcp_wmem = 4096 87380 16777216
•   min: 最小緩衝區大小
•   default: 初始緩衝區大小
•   max: 最大緩衝區大小

    緩衝區大小在 min 和 max 間動態調 整,初始大小爲 defaulttcp_wmem 中的 max 不能超過 net.core.wmem_max 的值:

sysctl net.core.wmem_max
net.core.wmem_max = 16777216

固定緩衝區設置

    有時應用程序會明確指定發送數據大小,可通過 setsockopt(2) 的 SO_SNDBUF 設置固定的緩衝區大小,此時 tcp_wmem 失效,緩衝區大小不再動態調整。SO_SNDBUF 的值也不能超過 net.core.wmem_max

TCP 連接總內存限制

系統中可能有大量 TCP 連接,總內存使用受 net.ipv4.tcp_mem 控制:

sysctl net.ipv4.tcp_mem
net.ipv4.tcp_mem = 94500000 915000000 927000000
•   min: 最小值
•   pressure: 內存壓力閾值
•   max: 最大內存

    達到 max 限制會影響數據發送。若 tcp_mem 限制觸發,可以使用以下命令觀察:

# 檢查系統是否支持 tracing,如果不支持需要先開啓,這裏不展開說明
ls /sys/kernel/debug/tracing

# 啓用 sock_exceed_buf_limit 事件追蹤
echo 1 > /sys/kernel/debug/tracing/events/sock/sock_exceed_buf_limit/enable

# 監控事件日誌
cat /sys/kernel/debug/tracing/trace_pipe

# 例如
tcp_sendmsg:  pid=3215 comm=nginx send buf exceed limit: sk=000000007b3ed4f0, pid=12345

# 分析結果並調整參數,例如
sysctl -w net.ipv4.tcp_wmem="4096 87380 16777216"
sysctl -w net.core.wmem_max=16777216

# 繼續觀察,確保沒有想過事件

# 停止事件追蹤
echo 0 > /sys/kernel/debug/tracing/events/sock/sock_exceed_buf_limit/enable

# 清理事件
echo > /sys/kernel/debug/tracing/trace

IP 層配置項

    在 IP 層,net.ipv4.ip_local_port_range 控制本地端口範圍。若默認範圍過小,可能導致連接創建失敗:

sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 1024 65535

    qdisc 是 Linux 內核的流控實現,txqueuelen 是 qdisc 隊列長度。隊列太小可能導致丟包,可使用以下命令檢查:

ip -s -s link ls dev eno1
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 3c:ec:ef:4e:15:06 brd ff:ff:ff:ff:ff:ff
    RX: bytes  packets  errors  dropped overrun mcast
    515151229849 1290891028 0       0       0       3029910
    RX errors: length   crc     frame   fifo    missed
               0        0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    397751986726 1242924130 0       0       0       0
    TX errors: aborted  fifo   window heartbeat transns
               0        0       0       0       4

    若 dropped 項不爲 0,可能需增大 txqueuelen

ifconfig eth0 txqueuelen 2000

    默認的 qdisc 爲 pfifo_fast,通常無需調整;若使用 TCP BBR 擁塞控制,需將其設置爲 fq

sysctl net.core.default_qdisc=fq

TCP 數據包的接收過程

摘自:https://time.geekbang.org/column/article/285816

    數據包到達網卡後會觸發中斷(IRQ)通知 CPU 讀取數據包。高性能網絡場景中數據包量大,頻繁中斷影響性能,NAPI 機制通過輪詢(poll)批量處理數據包。輪詢數量受 net.core.netdev_budget 控制:

sysctl net.core.netdev_budget
net.core.netdev_budget = 300

    可根據吞吐量需求適當調大此值,例如增至 600。調大此值會增加 CPU 輪詢時間,可能增加其他任務的調度延遲。

TCP 接收緩衝區

    TCP 接收緩衝區大小由 net.ipv4.tcp_rmem 控制:

sysctl net.ipv4.tcp_rmem
net.ipv4.tcp_rmem = 4096 87380 16777216
•   min: 最小緩衝區大小
•   default: 初始緩衝區大小
•   max: 最大緩衝區大小

    默認情況下,接收緩衝區大小動態調節。tcp_moderate_rcvbuf 控制動態調節開關,通常保持打開(值爲 1):

sysctl net.ipv4.tcp_moderate_rcvbuf
net.ipv4.tcp_moderate_rcvbuf = 1

    應用程序也可通過 SO_RCVBUF 設置固定的接收緩衝區大小。類似發送緩衝區,SO_RCVBUF 的值不能超過 net.core.rmem_max

sysctl net.core.rmem_max
net.core.rmem_max = 16777216

    注意:只有在 tcp_moderate_rcvbuf 爲 1,並且應用程序沒有通過 SO_RCVBUF 來配置緩衝區大小的情況下,TCP 接收緩衝區纔會動態調節。

BPF 工具觀測

    《BPF 性能之巔》一書第四章提到的 sormem 工具,用於跟蹤套接字接收隊列的大小,以直方圖的形式顯示與可調限制相比,接收隊列的滿載程度。如果接收隊列超出限制,數據包將被丟棄,從而導致性能問題。

/bpf-perf-tools-book-master/originals/Ch10_Networking# ./sormem.bt
Attaching 4 probes...
Tracing socket receive buffer size. Hit Ctrl-C to end.
^C
@rmem_alloc:
[0]                    2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[1]                    0 |                                                    |
[2, 4)                 0 |                                                    |
[4, 8)                 0 |                                                    |
[8, 16)                0 |                                                    |
[16, 32)               0 |                                                    |
[32, 64)               0 |                                                    |
[64, 128)              0 |                                                    |
[128, 256)             0 |                                                    |
[256, 512)             0 |                                                    |
[512, 1K)              0 |                                                    |
[1K, 2K)               0 |                                                    |
[2K, 4K)               2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
@rmem_limit:
[128K, 256K)           4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

    @rmem_alloc 顯示爲接收緩衝區分配了多少內存。@rmem_limit 是接收緩衝區的限制大小,使用 net.ipv4.tcp_rmem 進行調整。

    關於 bpftrace 的使用可以參考:《BPF 性能之巔》讀書筆記 - bpftrace 入門。書籍可以通過如下方式購買:

傳統工具 - ss 觀測

   命令行執行:

ss -tiepm4 | head
Failed to open cgroup2 by ID
State      Recv-Q Send-Q  Local Address:Port    Peer Address:Port        Process
ESTAB      0      0      192.168.103.35:48682 192.168.103.36:12005        users:(("mysqld",pid=1068110,fd=182)) ino:2779900121 sk:548dc90 cgroup:/machine.slice/docker.service <->
     skmem:(r0,rb6291456,t0,tb3497472,f4096,w0,o0,bl0,d78) ts sack cubic wscale:7,7 rto:220 rtt:18.805/20.763 ato:48 mss:4040 pmtu:4092 rcvmss:140 advmss:4040 cwnd:10 ssthresh:367 bytes_sent:21203293400 bytes_retrans:17832 bytes_acked:21203275569 bytes_received:1347875756 segs_out:21903559 segs_in:20225116 data_segs_out:21277948 data_segs_in:9598986 send 17.2Mbps lastsnd:188 lastrcv:188 lastack:148 pacing_rate 34.4Mbps delivery_rate 2.69Gbps delivered:21277949 app_limited busy:86620844ms rwnd_limited:4ms(0.0%) retrans:0/53 dsack_dups:53 rcv_rtt:1 rcv_space:282380 rcv_ssthresh:1841728 minrtt:0.016

結果解析:

3yvbvu

重點說明:

總結

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3pGiiWPLHQsVNzTjBy6lmg