乾貨!網絡丟包故障定位全景指南
引言
本期分享一個比較常見的⽹絡問題 -- 丟包。例如我們去 ping ⼀個⽹站,如果能 ping 通,且⽹站返回信息全⾯,則說明與⽹站服務器的通信是暢通的,如果 ping 不通,或者⽹站返回的信息不全等,則很可能是數據被丟包了,類似情況想必⼤家都不陌⽣。針對⽹絡丟包,本⽂提供⼀些常見的丟包故障定位⽅法,希望能夠幫助⼤家對⽹絡丟包有更多的認識,遇到丟包莫要慌,且跟着⼀起來漲姿 (知) 勢(識)···
什麼是丟包
數據在 Internet 上是以數據包爲單位傳輸的,單位爲字節,數據在⽹絡上傳輸,受⽹絡設備,⽹絡質量等原因的影響,使得接收到的數據⼩於發送出去的數據,造成丟包。
數據包接收、發送原理
發送數據包:
**
**
- 應⽤程序的數據包,在 TCP 層增加 TCP 報⽂頭,形成可傳輸的數據包。
- 在 IP 層增加 IP 報頭,形成 IP 報⽂。
- 經過數據⽹卡驅動程序將 IP 包再添加 14 字節的 MAC 頭,構成 frame(暫⽆ CRC),frame(暫⽆ CRC)中含有發送端和接收端的 MAC 地址。
- 驅動程序將 frame(暫⽆ CRC)拷貝到⽹卡的緩衝區,由⽹卡處理。
- ⽹卡爲 frame(暫⽆ CRC)添加頭部同步信息和 CRC 校驗,將其封裝爲可以發送的 packet,然後再發送到⽹線上,這樣說就完成了⼀個 IP 報⽂的發送了,所有連接到這個⽹線上的⽹卡都可以看到該 packet。
接收數據包:
- ⽹卡收到⽹線上的 packet,⾸先檢查 packet 的 CRC 校驗,保證完整性,然後將 packet 頭去掉,得到 frame。(⽹卡會檢查 MAC 包內的⽬的 MAC 地址是否和本⽹卡的 MAC 地址⼀樣,不⼀樣則會丟棄。)
- ⽹卡將 frame 拷貝到預分配的 ring buffer 緩衝。
- ⽹卡驅動程序通知內核處理,經過 TCP/IP 協議棧層層解碼處理。
- 應⽤程序從 socket buffer 中讀取數據。
核心思路
瞭解了收發包的原理,可以瞭解到丟包原因主要會涉及⽹卡設備、⽹卡驅動、內核協議棧三⼤類。以下我們將遵循 “從下到上分層分析(各層可能性出現的丟包場景),然後查看關鍵信息,最終得出分析結果” 的原則展開介紹。
** 目錄 -- 網絡丟包情形概覽**
**> 硬件網卡丟包
**
> 網卡驅動丟包
> 以太網鏈路層丟包
> 網絡 IP 層丟包
> 傳輸層 UDP/TCP 丟包
> 應用層 socket 丟包
針對以上 6 種情形,分別作出如下詳述~
硬件網卡丟****包
**Ring Buffer 溢出 **
如圖所示,物理介質上的數據幀到達後首先由 NIC(網絡適配器)讀取,寫入設備內部緩衝區 Ring Buffer 中,再由中斷處理程序觸發 Softirq 從中消費,Ring Buffer 的大小因網卡設備而異。當網絡數據包到達(生產)的速率快於內核處理(消費)的速率時,Ring Buffer 很快會被填滿,新來的數據包將被丟棄;
查看:
通過 ethtool 或 / proc/net/dev 可以查看因 Ring Buffer 滿而丟棄的包統計,在統計項中以 fifo 標識:
$ ethtool -S eth0|grep rx_fifo
rx_fifo_errors: 0
$ cat /proc/net/dev
Inter-|Receive | Transmitface |bytes packets errs drop fifo frame compressed
multicast|bytes packets errs drop fifo colls carrier compressed
eth0: 17253386680731 42839525880 0 0 0 0 0 244182022 14879545018057 41657801805 0 0 0 0 0 0
# 查看 eth0 網卡 Ring Buffer 最大值和當前設置
$ ethtool -g eth0
解決方案:修改網卡 eth0 接收與發送硬件緩存區大小
$ ethtool -G eth0 rx 4096 tx 4096
網卡端口協商丟包
- 查看網卡丟包統計:ethtool -S eth1/eth0
- 查看網卡配置狀態:ethtool eth1/eth0
主要查看網卡和上游網絡設備協商速率和模式是否符合預期;
解決方案:
1 重新自協商: ethtool -r eth1/eth0;
2 如果上游不支持自協商,可以強制設置端口速率:
ethtool -s eth1 speed 1000 duplex full autoneg off
網卡流控丟包
- 查看流控統計:
ethtool -S eth1 | grep control
rx_flow_control_xon 是在網卡的 RX Buffer 滿或其他網卡內部的資源受限時,給交換機端口發送的開啓流控的 pause 幀計數。對應的,tx_flow_control_xoff 是在資源可用之後發送的關閉流控的 pause 幀計數。
2 . 查看網絡流控配置:ethtool -a eth1
解決方案:關閉網卡流控
ethtool -A ethx autoneg off //自協商關閉
ethtool -A ethx tx off //發送模塊關閉
ethtool -A ethx rx off //接收模塊關閉
報文 mac 地址丟包
一般計算機網卡都工作在非混雜模式下,此時網卡只接受來自網絡端口的目的地址指向自己的數據,如果報文的目的 mac 地址不是對端的接口的 mac 地址,一般都會丟包,一般這種情況很有可能是源端設置靜態 arp 表項或者動態學習的 arp 表項沒有及時更新,但目的端 mac 地址已發生變化(換了網卡),沒有更新通知到源端(比如更新報文被丟失,中間交換機異常等情況);
查看:
-
目的端抓包,tcpdump 可以開啓混雜模式,可以抓到對應的報文,然後查看 mac 地址;
-
源端查看 arp 表或者抓包(上一跳設備),看發送的 mac 地址是否和下一跳目的端的 mac 地址一致;
解決方案:
-
刷新 arp 表然後發包觸發 arp 重新學習(可能影響其他報文,增加延時,需要小心操作);
-
可以在源端手動設置正確的靜態的 arp 表項;
其他網卡異常丟包
這類異常比少見,但如果都不是上面哪些情況,但網卡統計裏面任然有丟包計數,可以試着排查一下:
網卡 firmware 版本:
排查一下網卡 phy 芯片 firmware 是不是有 bug,安裝的版本是不是符合預期,查看 ethtool -i eth1:
和廠家提 case 詢問是不是已知問題,有沒有新版本等;
網線接觸不良:
如果網卡統計裏面存在 crc error 計數增長,很可能是網線接觸不良,可以通知網管排查一下:
ethtool -S eth0
解決方案:一般試着重新插拔一下網線,或者換一根網線,排查插口是否符合端口規格等;
報文長度丟包
網卡有接收正確報文長度範圍,一般正常以太網報文長度範圍:64-1518,發送端正常情況會填充或者分片來適配,偶爾會發生一些異常情況導致發送報文不正常丟包;
查看:
ethtool -S eth1|grep length_errors
解決方案:
1 調整接口 MTU 配置,是否開啓支持以太網巨幀;
2 發送端開啓 PATH MTU 進行合理分片;
簡單總結一下網卡丟包:
網卡驅動丟包
查看:ifconfig eth1/eth0 等接口
1.RX errors: 表示總的收包的錯誤數量,還包括 too-long-frames 錯誤,Ring Buffer 溢出錯誤,crc 校驗錯誤,幀同步錯誤,fifo overruns 以及 missed pkg 等等。
2.RX dropped: 表示數據包已經進入了 Ring Buffer,但是由於內存不夠等系統原因,導致在拷貝到內存的過程中被丟棄。
3.RX overruns: 表示了 fifo 的 overruns,這是由於 Ring Buffer(aka Driver Queue) 傳輸的 IO 大於 kernel 能夠處理的 IO 導致的,而 Ring Buffer 則是指在發起 IRQ 請求之前的那塊 buffer。很明顯,overruns 的增大意味着數據包沒到 Ring Buffer 就被網卡物理層給丟棄了,而 CPU 無法即使的處理中斷是造成 Ring Buffer 滿的原因之一,上面那臺有問題的機器就是因爲 interruprs 分佈的不均勻 (都壓在 core0),沒有做 affinity 而造成的丟包。
-
RX frame: 表示 misaligned 的 frames。
-
對於 TX 的來說,出現上述 counter 增大的原因主要包括 aborted transmission, errors due to carrirer, fifo error, heartbeat erros 以及 windown error,而 collisions 則表示由於 CSMA/CD 造成的傳輸中斷。
驅動溢出丟包
netdev_max_backlog 是內核從 NIC 收到包後,交由協議棧(如 IP、TCP)處理之前的緩衝隊列。每個 CPU 核都有一個 backlog 隊列,與 Ring Buffer 同理,當接收包的速率大於內核協議棧處理的速率時,CPU 的 backlog 隊列不斷增長,當達到設定的 netdev_max_backlog 值時,數據包將被丟棄。
查看:
通過查看 / proc/net/softnet_stat 可以確定是否發生了 netdev backlog 隊列溢出:
其中:每一行代表每個 CPU 核的狀態統計,從 CPU0 依次往下;每一列代表一個 CPU 核的各項統計:第一列代表中斷處理程序收到的包總數;第二列即代表由於 netdev_max_backlog 隊列溢出而被丟棄的包總數。從上面的輸出可以看出,這臺服務器統計中,並沒有因爲 netdev_max_backlog 導致的丟包。
解決方案:
netdev_max_backlog 的默認值是 1000,在高速鏈路上,可能會出現上述第二統計不爲 0 的情況,可以通過修改內核參數 net.core.netdev_max_backlog 來解決:
$ sysctl -w net.core.netdev_max_backlog=2000
單核負載高導致丟包
單核 CPU 軟中斷佔有高, 導致應用沒有機會收發或者收包比較慢,即使調整 netdev_max_backlog 隊列大小仍然會一段時間後丟包,處理速度跟不上網卡接收的速度;
查看:mpstat -P ALL 1
單核軟中斷佔有 100%,導致應用沒有機會收發或者收包比較慢而丟包;
解決方案:
- 調整網卡 RSS 隊列配置:
查看:ethtool -x ethx;
調整:ethtool -X ethx xxxx;
- 看一下網卡中斷配置是否均衡 cat /proc/interrupts
調整:
1) irqbalance 調整;
# 查看當前運行情況
service irqbalance status
# 終止服務
service irqbalance stop
2) 中斷綁CPU核 echo mask > /proc/irq/xxx/smp_affinity
- 根據 CPU 和網卡隊列個數調整網卡多隊列和 RPS 配置
-CPU 大於網卡隊列個數:
查看網卡隊列 ethtool -x ethx;
協議棧開啓 RPS 並設置 RPS;
echo $mask(CPU配置)> /sys/class/net/$eth/queues/rx-$i/rps_cpus
echo 4096(網卡buff)> /sys/class/net/$eth/queues/rx-$i/rps_flow_cnt
2)CPU小於網卡隊列個數,綁中斷就可以,可以試着關閉RPS看一下效果:
echo 0 > /sys/class/net/<dev>/queues/rx-<n>/rps_cpus
4.numa CPU 調整,對齊網卡位置,可以提高內核處理速度,從而給更多 CPU 給應用收包,減緩丟包概率;
查看網卡 numa 位置:
ethtool -i eth1|grep bus-info
lspci -s bus-info -vv|grep node
上面中斷和 RPS 設置裏面 mask 需要重新按 numa CPU 分配重新設置;
- 可以試着開啓中斷聚合(看網卡是否支持)
查看 :
ethtool -c ethx
Coalesce parameters for eth1:
Adaptive RX: on TX: on
stats-block-usecs: 0
sample-interval: 0
pkt-rate-low: 0
pkt-rate-high: 0
rx-usecs: 25
rx-frames: 0
rx-usecs-irq: 0
rx-frames-irq: 256
tx-usecs: 25
tx-frames: 0
tx-usecs-irq: 0
tx-frames-irq: 256
rx-usecs-low: 0
rx-frame-low: 0
tx-usecs-low: 0
tx-frame-low: 0
rx-usecs-high: 0
rx-frame-high: 0
tx-usecs-high: 0
tx-frame-high: 0
調整:
ethtool -C ethx adaptive-rx on
簡單總結一下網卡驅動丟包處理:
內核協議棧丟包
以太網鏈路層丟包
neighbor 系統 arp 丟包
arp_ignore 配置丟包
arp_ignore 參數的作用是控制系統在收到外部的 arp 請求時,是否要返回 arp 響應。arp_ignore 參數常用的取值主要有 0,1,2,3~8 較少用到;
查看:sysctl -a|grep arp_ignore
解決方案:根據實際場景設置對應值;
0:響應任意網卡上接收到的對本機 IP 地址的 arp 請求(包括環回網卡上的地址),而不管該目的 IP 是否在接收網卡上。
1:只響應目的 IP 地址爲接收網卡上的本地地址的 arp 請求。
2:只響應目的 IP 地址爲接收網卡上的本地地址的 arp 請求,並且 arp 請求的源 IP 必須和接收網卡同網段。
3:如果 ARP 請求數據包所請求的 IP 地址對應的本地地址其作用域(scope)爲主機(host),則不迴應 ARP 響應數據包,如果作用域爲全局(global)或鏈路(link),則迴應 ARP 響應數據包。
arp_filter 配置丟包
在多接口系統裏面(比如騰訊雲的彈性網卡場景),這些接口都可以迴應 arp 請求,導致對端有可能學到不同的 mac 地址,後續報文發送可能由於 mac 地址和接收報文接口 mac 地址不一樣而導致丟包,arp_filter 主要是用來適配這種場景;
查看:
sysctl -a | grep arp_filter
解決方案:
根據實際場景設置對應的值,一般默認是關掉此過濾規則,特殊情況可以打開;
0:默認值,表示迴應arp請求的時候不檢查接口情況;
1:表示迴應arp請求時會檢查接口是否和接收請求接口一致,不一致就不迴應;
arp 表滿導致丟包
比如下面這種情況,由於突發 arp 表項很多 超過協議棧默認配置,發送報文的時候部分 arp 創建失敗,導致發送失敗,從而丟包:
查看:
- 查看 arp 狀態:cat /proc/net/stat/arp_cache ,table_fulls 統計:
- 查看 dmesg 消息(內核打印):
dmesg|grep neighbour
neighbour: arp_cache: neighbor table overflow!
- 查看當前 arp 表大小:ip n|wc -l
查看系統配額:
sysctl -a |grep net.ipv4.neigh.default.gc_thresh
gc_thresh1:存在於ARP高速緩存中的最少層數,如果少於這個數,垃圾收集器將不會運行。缺省值是128。
gc_thresh2 :保存在 ARP 高速緩存中的最多的記錄軟限制。垃圾收集器在開始收集前,允許記錄數超過這個數字 5 秒。缺省值是 512。
gc_thresh3 :保存在 ARP 高速緩存中的最多記錄的硬限制,一旦高速緩存中的數目高於此,垃圾收集器將馬上運行。缺省值是1024。
一般在內存足夠情況下,可以認爲 gc_thresh3 值是 arp 表總大小;
解決方案:根據實際 arp 最大值情況(比如訪問其他子機最大個數),調整 arp 表大小
$ sudo sysctl -w net.ipv4.neigh.default.gc_thresh1=1024
$ sudo sysctl -w net.ipv4.neigh.default.gc_thresh2=2048
$ sudo sysctl -w net.ipv4.neigh.default.gc_thresh3=4096
$ sudo sysctl -p
arp 請求緩存隊列溢出丟包
查看:
cat /proc/net/stat/arp_cache ,unresolved_discards是否有新增計數
解決方案:根據客戶需求調整緩存隊列大小 unres_qlen_bytes:
網絡 IP 層丟包
接口 ip 地址配置丟包
- 本機服務不通,檢查 lo 接口有沒有配置地址是 127.0.0.1;
2 . 本機接收失敗, 查看 local 路由表:ip r show table local|grep 子機 ip 地址;這種丟包一般會出現在多 IP 場景,子機底層配置多 ip 失敗,導致對應 ip 收不到包而丟包;
解決方案:
-
配置正確接口 ip 地址;比如 ip a add 1.1.1.1 dev eth0
-
如果發現接口有地址還丟包,可能是 local 路由表沒有對應條目,緊急情況下,可以用手工補上:
比如 ip r add local 本機 ip 地址 dev eth0 table local ;
路由丟包
路由配置丟包
查看:
- 查看配置 路由是否設置正確(是否可達),是否配置策略路由(在彈性網卡場景會出現此配置)ip rule:
然後找到對應路由表。查看路由表:
或者直接用 ip r get x.x.x.x,讓系統幫你查找是否存在可達路由,接口是否符合預期;
- 查看系統統計信息:
netstat -s|grep "dropped because of missing route"
解決方案:重新配置正確的路由;
反向路由過濾丟包
反向路由過濾機制是 Linux 通過反向路由查詢,檢查收到的數據包源 IP 是否可路由(Loose mode)、是否最佳路由(Strict mode),如果沒有通過驗證,則丟棄數據包,設計的目的是防範 IP 地址欺騙攻擊。
查看:
rp_filter 提供三種模式供配置:
0 - 不驗證
1 - RFC3704 定義的嚴格模式:對每個收到的數據包,查詢反向路由,如果數據包入口和反向路由出口不一致,則不通過
2 - RFC3704 定義的鬆散模式:對每個收到的數據包,查詢反向路由,如果任何接口都不可達,則不通過
查看當前 rp_filter 策略配置:
$cat /proc/sys/net/ipv4/conf/eth0/rp_filter
如果這裏設置爲 1,就需要查看主機的網絡環境和路由策略是否可能會導致客戶端的入包無法通過反向路由驗證了。
從原理來看這個機制工作在網絡層,因此,如果客戶端能夠 Ping 通服務器,就能夠排除這個因素了。
解決方案:
根據實際網絡環境將 rp_filter 設置爲 0 或 2:
$ sysctl -w net.ipv4.conf.all.rp_filter=2或
$ sysctl -w net.ipv4.conf.eth0.rp_filter=2
防火牆丟包
客戶設置規則導致丟包
查看:
iptables -nvL |grep DROP ;
解決方案: 修改防火牆規則;
連接跟蹤導致丟包
連接跟蹤表溢出丟包
kernel 用 ip_conntrack 模塊來記錄 iptables 網絡包的狀態,並把每條記錄保存到 table 裏(這個 table 在內存裏,可以通過 / proc/net/ip_conntrack 查看當前已經記錄的總數),如果網絡狀況繁忙,比如高連接,高併發連接等會導致逐步佔用這個 table 可用空間,一般這個 table 很大不容易佔滿並且可以自己清理,table 的記錄會一直呆在 table 裏佔用空間直到源 IP 發一個 RST 包,但是如果出現被攻擊、錯誤的網絡配置、有問題的路由 / 路由器、有問題的網卡等情況的時候,就會導致源 IP 發的這個 RST 包收不到,這樣就積累在 table 裏,越積累越多直到佔滿。無論,哪種情況導致 table 變滿,滿了以後就會丟包,出現外部無法連接服務器的情況。內核會報如下錯誤信息:kernel: ip_conntrack: table full, dropping packet;
查看當前連接跟蹤數 :
cat /proc/sys/net/netfilter/nf_conntrack_max
解決方案:
增大跟蹤的最大條數
net.netfilter.nf_conntrack_max = 3276800
減少跟蹤連接的最大有效時間
net.netfilter.nf_conntrack_tcp_timeout_established = 1200
net.netfilter.nf_conntrack_udp_timeout_stream = 180
net.netfilter.nf_conntrack_icmp_timeout = 30
ct 創建衝突失導致丟包
查看:當前連接跟蹤統計:cat /proc/net/stat/nf_conntrack,可以查各種 ct 異常丟包統計
解決方案:內核熱補丁修復或者更新內核版本(合入補丁修改);
傳輸層 UDP/TCP 丟包
tcp 連接跟蹤安全檢查丟包
丟包原因:由於連接沒有斷開,但服務端或者 client 之前出現過發包異常等情況(報文沒有經過連接跟蹤模塊更新窗口計數),沒有更新合法的 window 範圍,導致後續報文安全檢查被丟包;協議棧用 nf_conntrack_tcp_be_liberal 來控制這個選項:
1:關閉,只有不在 tcp 窗口內的 rst 包被標誌爲無效;
0:開啓; 所有不在 tcp 窗口中的包都被標誌爲無效;
查看:
查看配置 :
sysctl -a|grep nf_conntrack_tcp_be_liberal
net.netfilter.nf_conntrack_tcp_be_liberal = 1
查看 log:
一般情況下 netfiler 模塊默認沒有加載 log,需要手動加載;
modprobe ipt_LOG11
sysctl -w net.netfilter.nf_log.2=ipt_LOG
然後發包後在查看 syslog;
解決方案:根據實際抓包分析情況判斷是不是此機制導致的丟包,可以試着關閉試一下;
分片重組丟包
情況總結:超時
查看:
netstat -s|grep timeout
601 fragments dropped after timeout
解決方法:調整超時時間
net.ipv4.ipfrag_time = 30
sysctl -w net.ipv4.ipfrag_time=60
frag_high_thresh, 分片的內存超過一定閾值會導致系統安全檢查丟包
查看:
netstat -s|grep reassembles
8094 packet reassembles failed
解決方案:調整大小
net.ipv4.ipfrag_high_thresh
net.ipv4.ipfrag_low_thresh
分片安全距檢查離丟包
查看:
netstat -s|grep reassembles
8094 packet reassembles failed
解決方案: 把 ipfrag_max_dist 設置爲 0,就關掉此安全檢查
pfrag_max_dist 特性,在一些場景下其實並不適用:
-
有大量的網絡報文交互
-
發送端的併發度很高,同時 SMP 架構,導致很容易造成這種亂序情況;
分片 hash bucket 衝突鏈太長超過系統默認值 128
查看:
dmesg|grep “Dropping fragment”
inet_frag_find: Fragment hash bucket 128 list length grew over limit. Dropping fragment.
解決方案:熱補丁調整 hash 大小;
系統內存不足,創建新分片隊列失敗
查看方法:
netstat -s|grep reassembles
8094 packet reassembles failed
dropwatch 查看丟包位置 :
解決方案:
a. 增大系統網絡內存:
net.core.rmem_default
net.core.rmem_max
net.core.wmem_default
b. 系統回收內存:
緊急情況下,可以用 /proc/sys/vm/drop_caches, 去釋放一下虛擬內存;
To free pagecache:
# echo 1 > /proc/sys/vm/drop_caches
To free dentries and inodes:
# echo 2 > /proc/sys/vm/drop_caches
To free pagecache, dentries and inodes:
echo 3 > /proc/sys/vm/drop_caches
MTU 丟包
查看:
-
檢查接口 MTU 配置,ifconfig eth1/eth0,默認是 1500;
-
進行 MTU 探測,然後設置接口對應的 MTU 值;
解決方案:
-
根據實際情況,設置正確 MTU 值;
-
設置合理的 tcp mss,啓用 TCP MTU Probe:
cat /proc/sys/net/ipv4/tcp_mtu_probing:
tcp_mtu_probing - INTEGER Controls TCP Packetization-Layer Path MTU Discovery.
Takes three values:
0 - Disabled
1 - Disabled by default, enabled when an ICMP black hole detected
2 - Always enabled, use initial MSS of tcp_base_mss.
tcp 層丟包
TIME_WAIT 過多丟包
大量 TIMEWAIT 出現,並且需要解決的場景,在高併發短連接的 TCP 服務器上,當服務器處理完請求後立刻按照主動正常關閉連接。。。這個場景下,會出現大量 socket 處於 TIMEWAIT 狀態。如果客戶端的併發量持續很高,此時部分客戶端就會顯示連接不上;
查看:
查看系統 log :
dmsg
TCP: time wait bucket table overflow;
查看系統配置:
sysctl -a|grep tcp_max_tw_buckets
net.ipv4.tcp_max_tw_buckets = 16384
解決方案:
-
tw_reuse,tw_recycle 必須在客戶端和服務端 timestamps 開啓時才管用(默認打開)
-
tw_reuse 只對客戶端起作用,開啓後客戶端在 1s 內回收;
-
tw_recycle 對客戶端和服務器同時起作用,開啓後在 3.5*RTO 內回收,RTO 200ms~ 120s 具體時間視網絡狀況。內網狀況比 tw_reuse 稍快,公網尤其移動網絡大多要比 tw_reuse 慢,優點就是能夠回收服務端的 TIME_WAIT 數量;
在服務端,如果網絡路徑會經過 NAT 節點,不要啓用 net.ipv4.tcp_tw_recycle,會導致時間戳混亂,引起其他丟包問題;
- 調整 tcp_max_tw_buckets 大小,如果內存足夠:
sysctl -w net.ipv4.tcp_max_tw_buckets=163840;
時間戳異常丟包
當多個客戶端處於同一個 NAT 環境時,同時訪問服務器,不同客戶端的時間可能不一致,此時服務端接收到同一個 NAT 發送的請求,就會出現時間戳錯亂的現象,於是後面的數據包就被丟棄了,具體的表現通常是是客戶端明明發送的 SYN,但服務端就是不響應 ACK。在服務器藉助下面的命令可以來確認數據包是否有不斷被丟棄的現象。
檢查:
netstat -s | grep rejects
解決方案:
如果網絡路徑會經過 NAT 節點,不要啓用 net.ipv4.tcp_tw_recycle;
TCP 隊列問題導致丟包
原理:
tcp 狀態機(三次握手)
**
**
協議處理:
一個是半連接隊列(syn queue):
在三次握手協議中,服務器維護一個半連接隊列,該隊列爲每個客戶端的 SYN 包開設一個條目 (服務端在接收到 SYN 包的時候,就已經創建了 request_sock 結構,存儲在半連接隊列中),該條目表明服務器已收到 SYN 包,並向客戶發出確認,正在等待客戶的確認包(會進行第二次握手發送 SYN+ACK 的包加以確認)。這些條目所標識的連接在服務器處於 Syn_RECV 狀態,當服務器收到客戶的確認包時,刪除該條目,服務器進入 ESTABLISHED 狀態。該隊列爲 SYN 隊列,長度爲 max(64,/proc/sys/net/ipv4/tcp_max_syn_backlog), 機器的 tcp_max_syn_backlog 值在 / proc/sys/net/ipv4/tcp_max_syn_backlog 下配置;
一個是全連接隊列(accept queue):
第三次握手時,當 server 接收到 ACK 報之後, 會進入一個新的叫 accept 的隊列,該隊列的長度爲 min(backlog, somaxconn),默認情況下,somaxconn 的值爲 128,表示最多有 129 的 ESTAB 的連接等待 accept(),而 backlog 的值則應該是由 int listen(int sockfd, int backlog) 中的第二個參數指定,listen 裏面的 backlog 可以有我們的應用程序去定義的;
查看:
連接建立失敗, syn 丟包:
netstat -s |grep -i listen
SYNs to LISTEN sockets dropped
也會受到連接滿丟包影響
解決方案: 增加大小 tcp_max_syn_backlog
連接滿丟包
-xxx times the listen queue of a socket overflowed
查看:
-
查看 accept 隊列大小 :net.core.somaxconn
-
ss -lnt 查詢 socket 隊列 :LISTEN 狀態: Recv-Q 表示的當前等待服務端調用 accept 完成三次握手的 listen backlog 數值,也就是說,當客戶端通過 connect() 去連接正在 listen() 的服務端時,這些連接會一直處於這個 queue 裏面直到被服務端 accept();Send-Q 表示的則是最大的 listen backlog 數值,這就就是上面提到的 min(backlog, somaxconn) 的值,
-
看一下是不是應用程序設置限制, int listen(int sockfd, int backlog);
解決方案:
-
Linux 內核參進行優化,可以緩解壓力 tcp_abort_on_overflow=1
-
調整 net.core.somaxconn 大小;
-
應用程序設置問題,通知客戶程序修改;
syn flood 攻擊丟包
目前,Linux 下默認會進行 5 次重發 SYN-ACK 包,重試的間隔時間從 1s 開始,下次的重試間隔時間是前一次的雙倍,5 次的重試時間間隔爲 1s, 2s, 4s, 8s, 16s,總共 31s,第 5 次發出後還要等 32s 都知道第 5 次也超時了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP 纔會把斷開這個連接。由於,SYN 超時需要 63 秒,那麼就給攻擊者一個攻擊服務器的機會,攻擊者在短時間內發送大量的 SYN 包給 Server(俗稱 SYN flood 攻擊),用於耗盡 Server 的 SYN 隊列。對於應對 SYN 過多的問題;
查看: 查看 syslog: kernel: [3649830.269068] TCP: Possible SYN flooding on port xxx. Sending cookies. Check SNMP counters.
解決方案:
-
增大 tcp_max_syn_backlog
-
減少 tcp_synack_retries
-
啓用 tcp_syncookies
-
啓用 tcp_abort_on_overflow, tcp_abort_on_overflow 修改成 1,1 表示第三步的時候如果全連接隊列滿了,server 發送一個 reset 包給 client,表示廢掉這個握手過程和這個連接(本來在 server 端這個連接就還沒建立起來);
PAWS 機制丟包
原理:PAWS(Protect Against Wrapped Sequence numbers),高帶寬下,TCP 序列號可能在較短的時間內就被重複使用 (recycle/wrapped)
就可能導致同一條 TCP 流在短時間內出現序號一樣的兩個合法的數據包及其確認包。
查看:
$netstat -s |grep -e "passive connections rejected because of time
stamp" -e "packets rejects in established connections because of
timestamp”
387158 passive connections rejected because of time stamp
825313 packets rejects in established connections because of timestamp
通過 sysctl 查看是否啓用了 tcp_tw_recycle 及 tcp_timestamp:
$ sysctl net.ipv4.tcp_tw_recycle
net.ipv4.tcp_tw_recycle = 1
$ sysctl net.ipv4.tcp_timestamps
net.ipv4.tcp_timestamps = 1
-
tcp_tw_recycle 參數。它用來快速回收 TIME_WAIT 連接,不過如果在 NAT 環境下會引發問題;
-
當多個客戶端通過 NAT 方式聯網並與服務端交互時,服務端看到的是同一個 IP,也就是說對服務端而言這些客戶端實際上等同於一個,可惜由於這些客戶端的時間戳可能存在差異,於是乎從服務端的視角看,便可能出現時間戳錯亂的現象,進而直接導致時間戳小的數據包被丟棄。如果發生了此類問題,具體的表現通常是是客戶端明明發送的 SYN,但服務端就是不響應 ACK。
解決方案:
在 NAT 環境下,清除 tcp 時間戳選項,或者不開啓 tcp_tw_recycle 參數;
TLP 問題丟包
TLP 主要是爲了解決尾丟包重傳效率的問題,TLP 能夠有效的避免較長的 RTO 超時,進而提高 TCP 性能,詳細參考文章:
http://perthcharles.github.io/2015/10/31/wiki-network-tcp-tlp/;
但在低時延場景下(短連接小包量),TLP 與延遲 ACK 組合可能會造成無效重傳,導致客戶端感發現大量假重傳包,加大了響應延遲;
查看:
查看協議棧統計:
netstat -s |grep TCPLossProbes
查看系統配置:
sysctl -a | grep tcp_early_retrans
解決方案:
- 關掉延遲 ack,打開快速 ack;
2.linux 實現 nodelay 語意不是快速 ack,只是關閉 nagle 算法;
- 打開快速 ack 選項,socket 裏面有個 TCP_QUICKACK 選項, 需要每次 recv 後再設置一次。
內存不足導致丟包
查看:
查看 log:
dmesg|grep “out of memory”
查看系統配置:
cat /proc/sys/net/ipv4/tcp_mem
cat /proc/sys/net/ipv4/tcp_rmem
cat /proc/sys/net/ipv4/tcp_wmem
解決方案:
根據 TCP 業務併發流量,調整系統參數,一般試着增大 2 倍或者其他倍數來看是否緩解;
sysclt -w net.ipv4.tcp_mem=
sysclt -w net.ipv4.tcp_wmem=
sysclt -w net.ipv4.tcp_rmem=
sysctl -p
TCP 超時丟包
查看:
抓包分析一下網絡 RTT:
用其他工具測試一下當前端到端網絡質量(hping 等);
# hping -S 9.199.10.104 -A
HPING 9.199.10.104 (bond1 9.199.10.104): SA set, 40 headers + 0 data bytes
len=46 ip=9.199.10.104 ttl=53 DF id=47617 sport=0 flags=R seq=0 win=0 rtt=38.3 ms
len=46 ip=9.199.10.104 ttl=53 DF id=47658 sport=0 flags=R seq=1 win=0 rtt=38.3 ms
len=46 ip=9.199.10.104 ttl=53 DF id=47739 sport=0 flags=R seq=2 win=0 rtt=30.4 ms
len=46 ip=9.199.10.104 ttl=53 DF id=47842 sport=0 flags=R seq=3 win=0 rtt=30.4 ms
len=46 ip=9.199.10.104 ttl=53 DF id=48485 sport=0 flags=R seq=4 win=0 rtt=38.7 ms
len=46 ip=9.199.10.104 ttl=53 DF id=49274 sport=0 flags=R seq=5 win=0 rtt=34.1 ms
len=46 ip=9.199.10.104 ttl=53 DF id=49491 sport=0 flags=R seq=6 win=0 rtt=30.3 ms
解決方案:
-
關閉 Nagle 算法,減少小包延遲;
-
關閉延遲 ack:
sysctl -w net.ipv4.tcp_no_delay_ack=1
TCP 亂序丟包
此時 TCP 會無法判斷是數據包丟失還是亂序,因爲丟包和亂序都會導致接收端收到次序混亂的數據包,造成接收端的數據空洞。TCP 會將這種情況暫定爲數據包的亂序,因爲亂序是時間問題(可能是數據包的遲到),而丟包則意味着重傳。當 TCP 意識到包出現亂序的情況時,會立即 ACK,該 ACK 的 TSER 部分包含的 TSEV 值會記錄當前接收端收到有序報文段的時刻。這會使得數據包的 RTT 樣本值增大,進一步導致 RTO 時間延長。這對 TCP 來說無疑是有益的,因爲 TCP 有充分的時間判斷數據包到底是失序還是丟了來防止不必要的數據重傳。當然嚴重的亂序則會讓發送端以爲是丟包一旦重複的 ACK 超過 TCP 的閾值,便會觸發超時重傳機制,以及時解決這種問題;詳細請參考博客:
https://blog.csdn.net/dog250/article/details/78692585
查看:抓包分析是否存在很多亂序報文:
解決方案:如果在多徑傳輸場景或者網絡質量不好,可以通過修改下面值來提供系統對 TCP 無序傳送的容錯率:
擁塞控制丟包
在互聯網發展的過程當中,TCP 算法也做出了一定改變,先後演進了
Reno、NewReno、Cubic 和 Vegas,這些改進算法大體可以分爲基於丟包和基於延時的擁塞控制算法。基於丟包的擁塞控制算法以 Reno、NewReno 爲代表,它的主要問題有 Buffer bloat 和長肥管道兩種,基於丟包的協議擁塞控制機制是被動式的,其依據網絡中的丟包事件來做網絡擁塞判斷。即使網絡中的負載很高,只要沒有產生擁塞丟包,協議就不會主動降低自己的發送速度。最初路由器轉發出口的 Buffer 是比較小的,TCP 在利用時容易造成全局同步,降低帶寬利用率,隨後路由器廠家由於硬件成本下降不斷地增加 Buffer,基於丟包反饋的協議在不丟包的情況下持續佔用路由器 buffer,雖然提高了網絡帶寬的利用率,但同時也意味着發生擁塞丟包後,網絡抖動性加大。另外對於帶寬和 RTT 都很高的長肥管道問題來說,管道中隨機丟包的可能性很大,TCP 的默認 buffer 設置比較小加上隨機丟包造成的 cwnd 經常下折,導致帶寬利用率依舊很低; BBR(Bottleneck Bandwidth and Round-trip propagation time)是一種基於帶寬和延遲反饋的擁塞控制算法。目前已經演化到第二版,是一個典型的封閉反饋系統,發送多少報文和用多快的速度發送這些報文都是在每次反饋中不斷調節。在 BBR 提出之前,擁塞控制都是基於事件的算法,需要通過丟包或延時事件驅動;BBR 提出之後,擁塞控制是基於反饋的自主自動控制算法,對於速率的控制是由算法決定,而不由網絡事件決定,BBR 算法的核心是找到最大帶寬(Max BW)和最小延時(Min RTT)這兩個參數,最大帶寬和最小延時的乘積可以得到 BDP(Bandwidth Delay Product), 而 BDP 就是網絡鏈路中可以存放數據的最大容量。BDP 驅動 Probing State Machine 得到 Rate quantum 和 cwnd,分別設置到發送引擎中就可以解決發送速度和數據量的問題。
Linux 4.9 內核首次採用 BBR 擁塞控制算法第一個版本,BBR 抗丟包能力比其他算法要強,但這個版本在某些場景下面有問題(缺點),BBR 在實時音視頻領域存在的問題,深隊列競爭不過 Cubic。
問題現象就是:在深隊列場景,BBR 的 ProbeRTT 階段只發 4 個包,發送速率下降太多會引發延遲加大和卡頓問題。
查看:
ss -sti //在源端 ss -sti|grep 10.125.42.49:47699 -A 3 ( 10.125.42.49:47699 是目的端地址和端口號)
解決方案:
-
ProbeRTT 並不適用實時音視頻領域,因此可以選擇直接去除,或者像 BBRV2 把 probe RTT 縮短到 2.5s 一次,使用 0.5xBDP 發送;
-
如果沒有特殊需求,切換成穩定的 cubic 算法;
UDP 層丟包
收發包失敗丟包
查看:netstat 統計
如果有持續的 receive buffer errors/send buffer errors 計數;
解決方案:
-
CPU 負載(多核綁核配置),網絡負載(軟中斷優化,調整驅動隊列 netdev_max_backlog),內存配置(協議棧內存);
-
按峯值在來,增大 buffer 緩存區大小:
net.ipv4.udp_mem = xxx
net.ipv4.udp_rmem_min = xxx
net.ipv4.udp_wmem_min = xxx
3. 調整應用設計:
-
UDP 本身就是無連接不可靠的協議,適用於報文偶爾丟失也不影響程序狀態的場景,比如視頻、音頻、遊戲、監控等。對報文可靠性要求比較高的應用不要使用 UDP,推薦直接使用 TCP。當然,也可以在應用層做重試、去重保證可靠性
-
如果發現服務器丟包,首先通過監控查看系統負載是否過高,先想辦法把負載降低再看丟包問題是否消失
-
如果系統負載過高,UDP 丟包是沒有有效解決方案的。如果是應用異常導致 CPU、memory、IO 過高,請及時定位異常應用並修復;如果是資源不夠,監控應該能及時發現並快速擴容
-
對於系統大量接收或者發送 UDP 報文的,可以通過調節系統和程序的 socket buffer size 來降低丟包的概率
-
應用程序在處理 UDP 報文時,要採用異步方式,在兩次接收報文之間不要有太多的處理邏輯
應用層 socket 丟包
socket 緩存區接收丟包
查看:
-
抓包分析是否存在丟包情況;
-
查看統計:
netstat -s|grep "packet receive errors"
解決方案:
調整 socket 緩衝區大小:
socket配置(所有協議socket):
# Default Socket Receive Buffer
net.core.rmem_default = 31457280
# Maximum Socket Receive Buffer
net.core.rmem_max = 67108864
具體大小調整原理:
緩衝區大小沒有任何設置值是最佳的,因爲最佳大小隨具體情況而不同
緩衝區估算原理:在數據通信中,帶寬時延乘積(英語:bandwidth-delay product;或稱帶寬延時乘積、帶寬延時積等)指的是一個數據鏈路的能力(每秒比特)與來回通信延遲(單位秒)的乘積。[1][2] 其結果是以比特(或字節)爲單位的一個數據總量,等同在任何特定時間該網絡線路上的最大數據量——已發送但尚未確認的數據。
BDP = 帶寬 * RTT
可以通過計算當面節點帶寬和統計平均時延來估算 BDP,即緩衝區的大小,可以參考下面常見場景估計:
參考:https://docs.oracle.com/cd/E56344_01/html/E53803/gnkor.html
應用設置 tcp 連接數大小丟包
查看:
請參考上面 TCP 連接隊列分析;
解決方案:
設置合理的連接隊列大小,當第三次握手時,當 server 接收到 ACK 報之後, 會進入一個新的叫 accept 的隊列,該隊列的長度爲 min(backlog, somaxconn),默認情況下,somaxconn 的值爲 128,表示最多有 129 的 ESTAB 的連接等待 accept(),而 backlog 的值則應該是由 int listen(int sockfd, int backlog) 中的第二個參數指定,listen 裏面的 backlog 可以有我們的應用程序去定義的;
應用發送太快導致丟包
查看統計:
netstat -s|grep "send buffer errors
解決方案:
-
ICMP/UDP 沒有流控機制,需要應用設計合理發送方式和速度,照顧到底層 buff 大小和 CPU 負載以及網絡帶寬質量;
-
設置合理的 sock 緩衝區大小:
setsockopt(s,SOL_SOCKET,SO_SNDBUF, i(const char*)&nSendBuf,sizeof(int));
- 調整系統 socket 緩衝區大小:
# Default Socket Send Buffer
net.core.wmem_default = 31457280
# Maximum Socket Send Buffer
net.core.wmem_max = 33554432
附:簡單總結一下內核協議棧丟包:
相關工具介紹
1.dropwatch 工具
原理: 監聽 kfree_skb(把網絡報文丟棄時會調用該函數)函數或者事件嗎,然後打印對應調用堆棧;想要詳細瞭解 linux 系統在執行哪個函數時丟包的話,可以使用 dropwatch 工具,它監聽系統丟包信息,並打印出丟包發生的函數:
- linux perf 工具監聽 kfree_skb:
sudo perf record -g -a -e skb:kfree_skb
sudo perf script
- tcpdump 工具
原理: tcpdump 是一個 Unix 下一個功能強大的網絡抓包工具,它允許用戶攔截和顯示發送或收到過網絡連接到該計算機的 TCP/IP 和其他數據包
抓包命令參考:
https://www.tcpdump.org/manpages/tcpdump.1.html
數據包分析:
-
用 wireshark 工具分析 參考:Wireshark 數據包分析實戰. pdf
-
可以轉化生成 CSV 數據,用 Excel 或者 shell 去分析特定場景報文;
-
可以在 linux 上用 tshark 命令行工具進行分析:
https://www.wireshark.org/docs/man-pages/tshark.html
- 報文重放復現
有些時候需要通過報文重放去復現丟包的場景(或者其他問題的場景)
修改報文爲正確地址:
tcpprep --port --pcap=eth1.pcap --cachefile=in.cache
tcprewrite --cachefile=in.cache -e 10.53.72.134:9.30.231.31 --enet-dmac=50:6b:4b:b4:9c:e7,88:df:9e:62:4c:4c --infile=eth1.pcap --outfile=out.pcap
重放:
tcpreplay -i eth1 -t out.pcap
使用參考:https://linux.die.net/man/1/tcpprep
這個時候方便在測試母機上再進一步分析,對現網影響比較小;
總結
本文只是分析大部分可能會丟包節點,提供了單個節點丟包排查和相關的解決方案, 丟包問題牽扯網絡鏈路各個組件,尤其是在雲網絡時代,網絡拓撲復雜多變,涉及運營商網絡,IDC 網絡,專線等 underlay 網絡,邊界網關,VPC 網絡,CLB 負載均衡等雲上 overlay 網絡,各種丟包問題排障起來非常複雜且困難,但掌握網絡通信基本原理後,可以分解網絡拓撲,對通信節點進行逐一排查,也可以找到丟包位置,後續會更加深入介紹雲計算時代,雲上網絡丟包排查方法,網絡架構解析等,達到任何丟包問題都可以快速排查和定位解決,幫助客戶快速恢復業務,下期再會。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/qAMAHRMXqXFiqxpNHsmLuw