深入分析 Linux 网络丢包问题.docx
所谓丢包,足指在网络数据的收发过程中,由于种种原因,数据包还没传输到应用程序中,就被丢作了.这些被丢弃包的数景,除以总的传输包数,也就是我们常说的丢包率。丢包率是网络性能中最核心的指标之一。丢包通常会带来严重的性能下降,特别是对TCp来说,丢包通常意味着网络拥塞和电传进而还会导致网络延迟增大、吞吐降低.一、哪里可能丢包接下来,我就以最常用的反向代理服务器Nginx为例,带你一起看看如何分析网络丢包的问题,执行下面的hping3命令,进一步验证Nginx是不是可以正常访问。这里我没有使用Ping,是因为ping基于ICMP协议,而Nginx使用的是TCP协议.110packetstransmitted,5packetsreceived,5%packetlossround-tripmin/avg/nax三3.0/609.7/3027.2ns从hping3的输出中,我们可以发现,发送了10个请求包,却只收到了5个回豆,50%的包都丢了.再观察每个请求的RTT可以发现,RTT也有非常大的波动变化,小的时候只有3ms,而大的时候则有3s根据这些输出,我们基本能判断,已经发生了丢包现象,可以猜测,3s的RTT,很可能是因为丢包后至传导致的.那到底是哪里发生了丢包呢?排查之前,我们可以回忆一下1.inux的网络收发流程,先从理论上分析,哪里有可能会发生丢包.你不妨拿出手边的笔和纸,边回忆边在纸上梳理,思考清楚再继续下面的内容.在这里,为了帮你理解网络丢包的原理,我画了一张图,你可以保存并打印出来使用从图中你可以看出,可能发生丢包的位置,实际上贯穿了整个网络协议栈.换句话说,全程都有丢包的可能. 在两台VM连接之间,可能会发生传输失败的错误,比如网络拥塞、线路错误等; 在网卡收包后,环形缓冲区可能会因为溢出而丢包; 在腌路屋,可能会因为网络帧校蛉失败、QoS等而丢包; 在IP展,可能会因为路由失败、组包大小超过MTU等而丢包; 在传输屣,可能会因为端口未监听、资源占用超过内核限制等而丢包; 在套接字层,可能会因为套接字缓冲区溢出而丢包; 在应用层,可能会因为应用程序异常而丢包; 此外,如果配背了iptables规则,这些网络包也可能因为iptables过谑规则而丢包当然,上面这些何瓦还有可能同时发生在通信的两台机器中.不过,由于我们没对YM2做任何修改,并旦VM2也只运行了一个最简单的hping3命令,这儿不妨假设它是没有问题的。为了简化整个扑杳过程,我们还可以进一步假设,VMl的网络和内核配置也没问题接卜.来,就可以从协议核中,逐层排查丢包问题。链路层当链路层由于缓冲区溢出等原因导致网卡丢包时,1.inux会在网卡收发数据的统计信息中记录下收发错误的次数.可以通过ethtool或者netstat,来百看网卡的丢包记录。3SRX-OK.RX-ERR,RX-DRP,RX-OVR,分别表示接收时的总包数、总错误数、进入RingBuffer后因其他原因(如内存不足)导致的丢包数以及RingBuffer溢出导致的丢包数.TX-OK.TX-ERR,TX-DRP.TX-OVR也代表类似的含义,只不过是指发送时对应的各个指标.这里我们没有发现任何错误,说明虚拟网卡没有丢包.不过要注意,如果用tc等工具配置了QoS,那么tc规则导致的丢包,就不会包含在网卡的统计信息中。所以接下来,我们还要检直一下eth上是否配置了tc规则,并查看有没有丢包.添加-S选项,以输出统计信息:disct80drootrefc11t2limit100loss3¾可以看到,eth上配百了一个网络模拟排队规则(qdiscnetem),并且配置了丢包率为30%(loss30%).再看后面的统计信息,发送了8个包,但是丢了4个。看来应该就是这里导致Nginx回复的响应包被netem模块给丢了.既然发现了问题,解决方法也很简单,直接删掉netem模块就可以了。执行下面的命令,删除tc中的netem模块:IOiscueiQeVx11vrooxeeJiloss删除后,由新执行之前的hping3命令,看看现在还有没有问题:不幸的是,从hping3的输出中可以看到还是50%的丢包,R11的波动也仍旧很大,从3ms到Is.品然,问题还是没解决,丢包还在继续发生不过,既然处路层已经持查完了,我们就继续向上层分析,籽右网洛层和传输层有没有问题.三、网络层和传输层在网络层和传输层中,引发丢包的因素非常多.不过,其实想确认是否丢包,是非常简单的事,因为IinUX已经为我们提供了各个协议的收发汇总情况,执行netstat-s命令,可以看到协议的收发汇总,以及错误信息:01CMPmessagesreceived/,(剑的ICNP2_llICMPincICMPoutputhiactiveconnectionopeninm:.assiveconnectionopeninco11n1connectionsestablished25segmentsreceivedI4segmentsretransmitted低传找文badsegmentsreceivedresetssent11reserruedVedforei,:.:.、:rHIfc'Kr(.acketheaderspr<dicttTCPTimeouts:£!时也TCPSynRetrans:4SYN小传数etstat汇总了IP、ICMP、TCP、UDP等各种协议的收发统计信息.不过,我们的目的是排直丢包问题,所以这里主要观察的是错误数、丢包数以及重传数.可以看到,只有TCP协议发生了丢包和重传,分别是:11次连接失败里试(11failedconnectionattempts)4次重传(4segmentsretransmitted)11次半连接重置(11resetsreceivedforembryonicSYN_RECVsockets)4次SYN正传(TCPSynRetranS)7次超时(TCPTimeouts)这个结果告诉我们,TCP协议有多次超时和失败重试,并Fl主要错误是半连接重置,换句话说,主要的失败,都是三次握手失败。不过,虽然在这儿看到了这么多失败.但具体失败的根源还是无法确定.所以,我们还需要维续顺着协议栈来分析.接下来的几层又该如何分析呢?四、iptables首先,除了网络层和传输层的各种协议,iptables和内核的连接跟踪机制也可能会导致丢包.所以,这也是发生丢包问题时我们必须要排查的一个因素。先来看看连接跟踪,要确认是不是连接跟踪导致的问题,只需要对比当前的连接跟踪数和最大连接跟踪数即可.filter.nfconntrackcounth182可以看到,连接跟踪数只有182,而最大连接跟踪数则是262144.显然,这里的丢包,不可能是连接跟踪导致的.接着,再来看iptables回顾一下iptables的原理,它基于Netfilter框架,通过一系列的规则,对网络数据包进行过流(如防火墙)和修改(如NAT).这些iptables规则,统一管理在一系列的表中,包括filter,nat、mangle(用于修改分组数据)和raw(用于原始数据包)等.而每张表又可以包括一系列的链,用于对iptables规则进行分组管理。对于丢包问题来说,最大的可能就是被filter表中的规则给丢弃了。要弄清楚这一点,就需要我们确认,那些目标为DROP和REJECT等会弃包的规则,有没有技执行到.可以亘接查询DROP和REJECT等规则的统计信息,看看是否为0.如果不是0,再把相关的规则冷出来进行分析.iptables-tfilter-v1.从iptables的埔出中,你可以看到,两条DROP规则的统计数值不是0,它们分别在INPUT和OUTPUT链中.这两条规则实际上是一样的,指的是使用statistic模块,进行随机30%的丢包.000.0/0表示匹配所有的源IP和目的IP,也就是会对所有包都迸行随机30%的丢包。看起来,这应该就是导致部分丢包的“罪魁祸首”了.执行下面的两条iptables命令,制除这两条DROP规则.18packetstransmitted,10packetsreceived,%packetlossroundtripminavgBaX33/79,15这次输出你可以看到,现在已经没有丢包了,并且延迟的波动变化也很小.看来,丢包问题应该已经解决了.不过,到目前为止,我们一直使用的hping3工具,只能验证案例Nginx的80端口处于正常监听状态,却还没有访问Nginx的HTTP服务.所以,不要匆忙下结论结束这次优化,我们还用要进一步确认,Nginx能不能正常响应HTTP请求.我们继续在终端二中,执行如下的curl命令,枪直Nginx对HTTP谙求的响应:curlOPeratfter360811奇怪,hpin3的结果显示MinX的80端1.l是正常状态,为什么还是不能正常响应HTTp清求呢?别忘了,我们还有个大杀器一一抓包操作。看来有必要抓包看看了。五、tcpdump执行下面的tcpdump命令,抓取80端口的包KCPdUePieth0ncPort80isteningOneth0.IinktypeEN10*B(Ethernet).CaPtUreSiZe262144然后,切换到终端二中,再次执行前面的curl命令:curl:(2B)Operationtimedoutafter等到curl命令结束后,再次切换回终端一,直看tcpdump的输出:Iength0:00.589½2SlfackR86808541,r|14:40:00,589894IP10.255.255.5.39058229.tionsnoTSval486883S41ecr25893768。1114:40:03,589417IP172.17.0.2,8>l,255.255.5.3958:Fla1,nop,TSval25093791ecr48680541,no.sack4:40:03.S89352IP10.255.25S.5.39S8>172.17.0.tionsnop,nop.TSVaI486800541ecr2509376001tions从tcpdump的输出中,我们就可以看到:前三个包是正常的TCP三次握手,这没问题;但第四个包却是在3眇以后了,并且还是客户端(VM2)发送过来的FIN包,说明客户端的连接关闭了根据curl设置的3秒超时选项,你应该能猜到,这是因为curl命令超时后退出了。用Wireshark的FlowGraph来表示,你可以更清甦地看到上面这个问题:Time192.168.0.2192.168.030Comment0.000000SYNQfSeq三00.0000340*OT7AAfSYN.ACKOVOASe三0Acka10.001976,17RACKOVOAS«q«1Ackr13.000520FIN.ACKOVSeq三76Ack13.00054537446ACKOV80Seq1Ack1这里比较奇怪的是,我们并没有抓取到curl发来的HTTPGET请求.那究竟是网卡丢包了,还是客户端就没发过来呢?可以由新执行netstat-i命令,确认一下网卡有没有丢包问题:kernelInterfacetablelIlface;RX-OKRXERRRX-ORPRXOVR*'J三,»4ERR«X,DRrTXOVRaSS从netstat的城出中,你可以看到,接收丢包数(RX-DRP)是344,果然是在网卡接收时丢包了.不过问题也来了,为什么刚才用hping3时不丢包,现在换成GET就收不到了呢?还是那句话,遇到搞不懂的现象,不妨先去直查工具和方法的原理.我们可以对比一下这两个工具:hping3实际上只发送了SYN包;curl在发送SYN包后,还会发送HTTPGET诘求.HTTPGET本质上也是一个TCP包,但跟SYN包相比,它还携带了HTTPGET的数据。通过这个对比,你应该想到了,这可能是MTU配SS错误导致的。为什么呢?其实,仔细观察上面netstat的输出界面,第二列正是每个网卡的MTU值.eth的MTU只有100,而以太网的MTU默认值是1500,这个100就显得太小了.当然,MTU问地是很好解决的,把它改成1500就可以了.修改完成后,再切换到终端二中,再次执行curl命令,确认问迹是否真的解决了:html非常不容易呀,这次终于看到了熟悉的Nginx响应,说明丢包的问题终于彻底解决了.