Linux网络丢包排查 - 墨天轮
一、简介
工作中遇到的服务器,最常用的操作系统就是linux系统,linux 系统使用网络适配器和外部进行数据交换。当在高速链路或异常环境下进行网络通信时,就有可能出现网络数据丢包现象,接下来我主要要说的是:网路丢包的故障定位思路和解决方法。
二、相关原理介绍
1 、网络消息的收发(报文收发过程)
在说丢包故障定位之前,我先来了介绍“网络报文收发过程”。本文以接收报文为例,发送报文与之类似,只是报文的传输方向相反。
1、网络packet首先通过网线被网卡获取,网卡检查packet的crc正常后,去掉packet头得到frame,如果frame中MAC的目的地址为本机地址,则接受该报文,否则丢弃(在混杂模式下也会接收该报文)
2、网卡将frame拷贝到网卡内部缓冲区中,一般是网卡的ring buffer中,拷贝完成后触发软中断通知内核处理
3、内核从ring buffer中拷贝网络数据,并传递给网络协议栈进行解析
4、协议栈解析完成后将数据放入ocket套接字的buffer中,最终传递给上层应用
2 、相关名词解释
-
Bash代码
enp125s0f0:flags=4163 mtu 1500
inet90.90.160.163 netmask 255.255.252.0 broadcast 90.90.163.255
inet6fe80::903a:4e71:69cd:eb09 prefixlen 64 scopeid 0x20
ether08:4f:0a:04:85:ac txqueuelen 1000 (Ethernet)
RXpackets 28356 bytes 4397271 (4.1 MiB)
RXerrors 0 dropped 15869 overruns 0 frame 0
TXpackets 3003 bytes 450378 (439.8 KiB)
TXerrors 0 dropped 0 overruns 0 carrier 0 collisions 0
RX errors:表示总的收包的错误数量
RX dropped:表示数据包已进入Ring buffer,但是由于系统原因(如内存不足)导致在拷贝到内存中的过程中被丢弃
RX overruns:表示数据还未进入网卡缓存(Ring buffer)时就被丢弃了,一般是由于Ring buffer中的数据未被及时取出导致溢出,新来的数据只能被丢弃。例如CPU负载大,导致处理网卡数据的速度小于网卡接收数据的速度,Ring buffer溢出。
RX frame:表示misaligend的frames数量
三、丢包故障定位与解决
1 、网卡丢包
首先检查丢包是否是因为crc校验错误导致的:
Bash代码
[root@localhost ~]# ethtool -S enp1s0f0 | grep crc
rx_crc_errors_phy:0
如果crc字段为非0,则表示网络报文在传输时出现了差错,此时可以更换线缆或网卡再做验证。
如果丢包发生在网卡上,则可以通过ethtool -S eth0 | egrep-i drop|error确认
Bash代码
| | | |
[root@localhost ~]# ethtool -S enp125s0f0 | egrep -ierror|drop
rxq#0_rx_dropped: 0
rxq#0_errors: 0
rx_oq_drop_pkt_cnt:0
netstat -i也可以提供网卡的收发报文和丢包情况,正常情况下error、drop和overrun字段应该为0
Bash代码
如果RX_OVR一直在增加,说明Ringbuffer有溢出,除了Ring Buffer太小以外,有可能是CPU处于高负荷下,来不及从Ring buffer中获取数据,此时可以检查CPU高负荷的原因,对网卡进行中断亲和设置等操作。通过查看/proc/net/dev也可以查看是否有Ring buffer满而导致的丢包
[root@localhost~]# netstat -i
KernelInterface table
IfaceMTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
enp125s0f01500 29113 0 16149 0 3258 0 0 0 BMRU
enp125s0f11500 0 0 0 0 0 0 0 0 BMU
enp125s0f21500 0 0 0 0 0 0 0 0 BMU
enp125s0f31500 0 0 0 0 0 0 0 0 BMRU
enp1s0f01500 0 0 0 0 0 0 0 0 BMU
enp1s0f11500 0 0 0 0 0 0 0 0 BMU
lo65536 1820 0 0 0 1820 0 0 0 LRU
如果硬件或者驱动没有问题,一般网卡丢包是由于Ring buffer太小导致,可以使用ethtool -G修改Ring buffer大小。
Bash代码
[root@localhost~]# cat proc/net/dev
Inter-|Receive | Transmit
face|bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
enp125s0f1:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
enp125s0f3:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
enp125s0f2:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
enp1s0f1:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
enp1s0f0:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
lo:169176 1948 0 0 0 0 0 0 169176 1948 0 0 0 0 0 0
enp125s0f0:4880071 32037 0 18238 0 0 0 40408 486664 3302 0 0 0 0 0 0
2 、内核丢包其中的fifo字段统计的是Ringbuffer满而丢弃的包
内核从网卡收到数据以后,交给协议栈处理之前会有缓冲队列backlog,每个CPU都有一个backlog队列,当从网卡中获取数据到backlog中的速率大于从backlog中将数据交给协议栈的速率时就会发生溢出。可以查看/proc/net/softnet_stat文件确认是否有backlog溢出:
-
Bash代码
[root@localhost~]# cat proc/net/softnet_stat
0000000300000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
-
每一行代表一个CPU core接收数据的情况
-
第1列表示收到的包总数
-
第2列是丢弃的包计数,此处的丢包指的是从网卡Ring buffer中输出到内核缓存队列时,由于队列满了而丢弃的数据包
-
第3列表示软中断一次取走netdev_budget个数据包,或取数据包时间超过2ms的次数
-
第4列~第8列固定为0,没有意义
-
第9列表示发送数据包时,对应的队列被锁住的次数
如果是因为backlog队列溢出导致的丢包,可以修改backlog队列的大小,通过systctl修改netdev maxbacklog参数,默认大小为1000
-
Bash代码
sysctl -w net.core.netdev_max_backlog=2000 |
在将数据交给内核协议栈后,协议栈进行分析处理。在协议栈中也有可能丢包,通过netstat-s可以查看是否有协议栈丢包,-u参数指定udp协议,-t参数指定tcp协议,也可以加-c参数持续输出,看各个数据的变化,本文以UDP协议为例进行讲解
-
Bash代码
[root@localhost~]# netstat -s -u
IcmpMsg:
InType3:1006
OutType3:1006
Udp:
0packetsreceived
1006packetsto unknown port received.
0packetreceive errors
1081packetssent
0receivebuffer errors
0sendbuffer errors
UdpLite:
IpExt:
InMcastPkts:3245
OutMcastPkts:11
InBcastPkts:6213
InOctets:2859135
OutOctets:633832
InMcastOctets:495247
OutMcastOctets:2658
InBcastOctets:2038580
InNoECTPkts:13895
[root@localhost~]#
-
packet receive errors表示接收有丢包
-
packets to unknown port received表示系统接收到的UDP报文的目标端口没有应用在监听,一般影响不严重
-
receive/send buffer errors表示收发队列太小导致的丢包数量
对于收发队列太小导致的丢包,可以通过调整收发队列参数来解决,系统默认的receive/sendbuffer大小如下:
-
Bash代码
[root@localhost ~]# sysctlnet.core.rmem_default
net.core.rmem_default =229376
[root@localhost ~]# sysctlnet.core.wmem_default
net.core.wmem_default =229376
可以使用以下命令修改buffer大小
-
Bash代码
sysctl-w net.core.rmem_max=26214400# 设置为 25M
sysctl -w net.core.wmem_max=26214400# 设置为 25M
通过查看/proc/net/snmp文件也可以查看各个协议的收发包情况:
-
Bash代码
Ip: Forwarding DefaultTTL InReceives InHdrErrorsInAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequestsOutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKsFragFails FragCreates
Ip: 2 64 14757 0 28 0 0 0 4844 5745 512 0 0 0 0 0 0 00
Icmp: InMsgs InErrors InCsumErrors InDestUnreachsInTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoRepsInTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrorsOutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchosOutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps
Icmp: 1038 0 0 1038 0 0 0 0 0 0 0 0 0 0 1038 0 1038 00 0 0 0 0 0 0 0 0
IcmpMsg: InType3 OutType3
IcmpMsg: 1038 1038
Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpensPassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegsInErrs OutRsts InCsumErrors
Tcp: 1 200 120000 -1 0 2 0 0 2 2768 3592 0 0 0 0
Udp: InDatagrams NoPorts InErrors OutDatagramsRcvbufErrors SndbufErrors InCsumErrors IgnoredMulti
Udp: 0 1038 0 1113 0 0 0 0
UdpLite: InDatagrams NoPorts InErrors OutDatagramsRcvbufErrors SndbufErrors InCsumErrors IgnoredMulti
UdpLite: 0 0 0 0 0 0 0 0/
3 、应用丢包
内核协议栈把接收到的报文放到socket套接字的buffer中,应用程序从buffer中不断读取报文。所以这里有两个和应用程序有关的因素会影响丢包:socket buffer的大小和应用程序报文读取速度。
socket buffer大小可在应用程序初始化socket时设置,不过增大buffer的值会增加内存的使用,请根据实际情况配置;对于应用程序报文处理速度,应采用异步方式处理
四、其他定位方法
1 、dropwatch
dropwatch可以输出数据包是在哪个内核函数中丢失的:
-
Bash代码
[root@localhost~]# dropwatch -l kas
Initalizingkallsyms db
dropwatch>start
Enablingmonitoring...
Kernelmonitoring activated.
IssueCtrl-C to stop monitoring
1drops at skb_queue_purge+20 (0xffff000008708d3c)
2drops at __netif_receive_skb_core+504(0xffff00000871bd7c)
1drops at __netif_receive_skb_core+504(0xffff00000871bd7c)