# 24 | 丢包:如何确定丢包的存在及其程度? 你好,我是胜辉。 在上一讲里,我们回顾了一个网络路径排查的典型案例。我们是通过 **nc工具**发起不同源端口的连接,从而定位了ECMP路径中的问题。这个排查方法的背后,其实是我们对三层网络的深入理解和灵活应对。在这一类的网络排查中,我们都未必要上抓包分析这样的“重型武器”,只要场景合适,我们就可以用小工具达到大效果。 现在我们也知道了,这个案例的根因是ECMP路径中某个节点存在**丢包**。而丢包,也是网络排查中特别普遍的现象,特别是下面这三个问题: * 有没有丢包? * 在哪里丢包? * 丢包程度多严重? 这三个问题的组合,就使得很多故障场景变得复杂。特别是当丢包情况不太明显的时候,问题表象就变得更加“神出鬼没”了。 所以在这一讲里,我们将会对“丢包”这个十分典型的问题场景进行一次深入的探讨。这样,下次你遇到丢包等问题的时候,就有很多种“兵器”,也知道在什么场景下使用它们,从而真正突破“丢包”这个难点了。 那么首先,在讨论丢包之前,我们要先对网络排查工具做一下总体的审视。 ## 路径排查工具概览 网络路径排查的工具有挺多,大体上可以分为两大类:探测类工具和统计类工具。 为什么要这么分呢?在我看来,网络信息的获取方式,大体上有动态和静态之分: * **动态**的信息在传输过程中体现,传输结束后就没有了。它获得的是通信两端在动态变化中的网络状况,所以我们需要做实时的探测,才能把这个动态的状态抓取下来。 * **静态**的信息在传输结束后依然存在,继续保存在通信端的系统中。它是通信端的网络信息的历史统计。我们只要通过运行统计类工具,就能把这种累计的信息读取出来了。 所以下面,我们就先来看看这两类工具具体都包括了什么。 ### 探测类工具 探测类工具主要包括ping、traceroute、mtr、nc、telnet等,它们都是从一端发起,对另外一端发送探测报文,然后观测报文的丢失、乱序、时延等情况。 其中,ping、traceroute、mtr主要是利用ICMP或者UDP的特性,实现了**对网络路径状况的检测**。在接下来的课程中,我们会深入探讨traceroute和mtr的工作原理。 而nc和telnet,则主要是测试**传输层连通性**的,比较典型的场景就是探测TCP握手能否成功。有了这两个工具,我们不需要做tcpdump抓包,就可以检测出TCP端口是否可以连通了。 ### 统计类工具 统计类工具包括netstat、ss,这些工具都是在一端读取自己的历史统计值,而并不发送出探测报文。比如,我们可以通过netstat -s命令,看到很详细的传输层、网络层、数据链路层等的质量状况,包括报文丢失、重传、重置(RST)等情况的统计。比如下面这样: ```bash # netstat -s Ip:     Forwarding: 1     41406 total packets received     0 forwarded     0 incoming packets discarded     41406 incoming packets delivered     30976 requests sent out Icmp:     16 ICMP messages received     0 input ICMP message failed     ICMP input histogram:         echo replies: 16     16 ICMP messages sent     0 ICMP messages failed     ICMP output histogram:         echo requests: 16 IcmpMsg:         InType0: 16         OutType8: 16 Tcp:     18 active connection openings     0 passive connection openings     0 failed connection attempts     1 connection resets received     2 connections established     41353 segments received     30924 segments sent out     0 segments retransmitted     0 bad segments received     0 resets sent Udp:     37 packets received     0 packets to unknown port received     0 packet receive errors     37 packets sent     0 receive buffer errors     0 send buffer errors ...... ``` 其中,跟丢包直接相关的是 **segments retransmitted** 这个指标,**如果它一直在增长,那一般意味着网络上存在丢包**,我们需要多加注意了。 当然,netstat命令是相对传统的命令,属于早已停止维护的net-tools工具集。新的工具集是iproute2,其中包括了ip、ss、nstat等命令。这些命令也可以读取到类似的网络统计信息。 不过,既然新老命令的功能类似,那为什么我们还要重复造这个轮子呢? 这其实是因为,netstat主要是通过/proc文件系统收集信息的,而ss主要通过netlink内核接口获取数据(有些信息也依然要从/proc中读取),这个netlink接口的效率要比/proc接口更高,所以ss能更快地返回数据。 另外,ss能获得的信息要比netstat更丰富。比如,ss就可以获取到socket option的信息,但netstat就做不到。 我们可以看一个例子。我在自己的Ubuntu容器里运行了这条命令: ```bash $ ss -eit ``` 就可以获取到很多netstat无法获取的信息: ![图片](https://static001.geekbang.org/resource/image/07/8b/07c095c22467de0d73b982184c0db98b.jpg?wh=1464x244) 我们可以挑几个信息展开一下。 * `cubic`:这条TCP连接用的拥塞控制算法是cubic。 * `wscale:2,7`:这条TCP连接的两端的Window Scale分别是2和7。 * `cwnd:10`:这条TCP连接的拥塞窗口为10个MSS。 如果你感到好奇:“ss是如何获取到这么多有用的信息的呢?”其实这个也不难。一个简单的办法就是使用[第21讲](https://time.geekbang.org/column/article/493040)我们介绍的strace,然后就能观察到ss -eit拿到这些信息的具体过程了。 > 其实,类似这样的细微的探究过程,都能不经意间不断深化我们的技术能力。 #### nstat工具 我们还可以了解一下nstat这个工具。它跟ss一样,也是iproute2工具包里面的。nstat的一个特点是,如果不加参数,**它每次运行时输出的数值,是从上一次被执行以来这些计数器的变化值**。这就带来了一个挺明显的优势:我们不用像使用netstat那样,在两次输出值中找出变化量了,nstat输出的直接就是变化量。 首次运行nstat时,输出的就是全部计数器的值。第二次运行时,就只是发生变化的数值了,比如下面的nstat输出的就是变化的值: ```bash root@08d984197cfb:/# nstat #kernel IpInReceives                    5                  0.0 IpInDelivers                    5                  0.0 IpOutRequests                   4                  0.0 TcpEstabResets                  1                  0.0 TcpInSegs                       5                  0.0 TcpOutSegs                      4                  0.0 TcpOutRsts                      3                  0.0 IpExtInOctets                   336                0.0 IpExtOutOctets                  160                0.0 IpExtInNoECTPkts                5                  0.0 ``` 当然,要查看全部值也是可以的,执行这条命令即可: ```bash nstat -a ``` nstat还有个功能是按JSON格式做输出,这有助于我们做自动化运维。做法是nstat命令加上`--json`参数: ```bash nstat --json ``` 而既然netstat和ss都能读取到计数器的值,那么**这些计数器本身又是如何产生的呢?** #### 内核的SNMP计数器 这些计数器的统计和更新其实都是Linux内核的行为。内核根据[RFC1213](https://datatracker.ietf.org/doc/html/rfc1213)的标准,定义了IP、ICMP、TCP、UDP等协议相关的[SNMP计数器](https://www.kernel.org/doc/html/latest/networking/snmp_counter.html)。在每次报文发送、接收、重传等行为发生时,都会往相应的计数器中更新数值。具体来说,这些计数器的定义在内核代码的include/linux/snmp.h文件中。 > 补充:SNMP名义上是“简单网络管理协议”,其实名字叫“简单”的技术往往并不简单。事实上SNMP还是很庞大的,这里就不展开了。 所以说,这些数值都不是netstat、nstat这些命令去“生成”的,而是内核早就准备好这些数据了,工具去完成读取就好了。 ![](https://static001.geekbang.org/resource/image/e7/b1/e77af0a0d27bfcf2a1790844bf4408b1.jpg?wh=2000x740) 好了,讨论完各种工具,接下来我们就进入丢包这个核心话题。 ## 如何确定丢包位置和丢包率? 在丢包这个问题上,我们最关心的可能就是**丢包在哪里、丢包率是多少**这两个问题。这里我们重点使用的工具,是traceroute和mtr。 ### traceroute和mtr的工作原理 我们先说traceroute。其实在[第1讲](https://time.geekbang.org/column/article/477510),我就提到过traceroute的两种模式:UDP模式和ICMP模式。但是,**traceroute为什么可以做到探测网络路径中的节点的呢?**要知道,ping也是用ICMP,它咋就看不到中间节点呢? 你可以找一个工程师问这个问题:“traceroute是如何显示每一个节点的?”很多人可能都会卡住,或者会给一个似是而非的答案,比如说:“可能是有什么协议吧,可以展示每一个节点的”。 > 所以我们不要小看一些表面上不起眼的小技术点,也许里面藏着的,还正是你的盲区,也是我们可以提高的地方。 实际上,traceroute的工作原理,是巧妙地利用了IP报文的 **TTL属性**。具体过程是这样的: * 发送端首次探测时把UDP源端口为33434,TTL设置为1。这个报文经过第一个路由器时TTL会被减1,也就是变为0,因此包被丢弃,这个路由器向源地址发回一个ICMP超时通知(ICMP Time Exceeded Message),于是第一跳就被探测出来了。 * 发送端把下一次发送的包的UDP源端口也加一,变为33435,TTL也在原来的基础加一,变为2,这样就可以多前进一步。跟上面的步骤类似,第二跳也被探测出来了。 * 发送端一直重复TTL加一和UDP源端口加一的操作,直到报文最终到达目的地,后者回复一个端口不可达的ICMP错误信息(ICMP Port Unreachable)。当源地址收到这个消息时,就停止traceroute。 > 补充:实际工作中,traceroute会批量发送多个不同TTL值的报文然后等待回应,这样效率更高。 当然,在探测过程中,任何一个探测报文如果在限定时间内没有收到,traceroute就会认为超时,在输出中这一跳就显示为一个星号。 另外,目的地也可能不回复ICMP Port unreachable,那么traceroute的输出就会出现从某一个位置开始的后续跳数全部是星号,直到你按Ctrl + C终止探测,或者是直到默认的64跳检测全部完成。对于这种情况,你可以**加上-I参数**,让traceroute用ICMP协议的echo request消息进行探测,一般来说,**目的地总能对UDP和ICMP中的一种进行响应**。 我们看一下原理示意图: ![](https://static001.geekbang.org/resource/image/4a/89/4a7cf207be80c4f0c628f86766ed3889.jpg?wh=2000x1125) Linux的traceroute命令默认用的就是UDP模式,而Windows的tracert默认用了ICMP模式。有时候我们会看到每一跳有2个或者3个节点IP,其实就表明这几个路径是启用了ECMP的,所以有多个节点会被选到。比如下面这个例子: ```bash $ traceroute www.ebay.com traceroute to e9428.a.akamaiedge.net (23.45.61.92), 64 hops max   1   10.0.2.2  0.308ms  0.192ms  0.353ms   2   192.168.1.1  4.284ms  1.379ms  1.360ms   3   100.65.0.1  5.916ms  5.464ms  5.520ms   4   61.152.53.149  148.051ms  146.005ms  4.126ms   5   61.152.25.2  16.216ms  61.152.25.101 14.618ms  144.713ms   6   *  202.97.83.13  8.385ms  *   7   202.97.12.186  5.112ms  13.341ms  14.592ms   8   202.97.41.142  49.747ms  54.797ms  202.97.41.130 58.370ms   9   *  *  *  10   *  *  *  11   *  *  *  12   *  *  *  13   *  *  * ``` 当你改成用ICMP模式后,每一跳就变成只有一个IP了。原因我们在上一讲讨论过,就是因为五元组是一致的,所以路径也不变。比如我们还是对www.ebay.com进行探测,这次用ICMP模式,每跳就只有一个IP了,而且目的地也返回了有效的响应。 ```bash $ traceroute -I www.ebay.com traceroute to e9428.a.akamaiedge.net (23.45.61.92), 64 hops max   1   10.0.2.2  0.003ms  0.002ms  0.003ms   2   192.168.1.1  7.616ms  1.315ms  6.206ms   3   *  100.65.0.1  11.057ms  7.746ms   4   *  61.152.53.149  18.436ms  9.598ms   5   *  61.152.25.158  12.918ms  *   6   *  *  *   7   *  *  *   8   202.97.41.142  50.746ms  58.052ms  59.546ms   9   203.215.237.22  62.385ms  66.607ms  54.561ms  10   23.45.61.92  49.609ms  46.586ms  45.567ms ``` ### 用mtr定位丢包点 在上一讲的案例中,我们是用nc加上-p参数指定多个源端口来测试,定位到了问题路径的存在。这对于问题节点丢包率比较高时,作用比较明显。 但是,当问题节点的丢包率不高的时候,我们用nc做多次测试,未必正巧能抓到现场。我们最好还要有**持续监控**的手段,运行一段时间或者发送一定数量(比如1000次以上)的报文,通过足够的探测数,来抓到偶发的故障现象。 这里呢,我们就要用到预习篇提到过的工具:**mtr**。mtr相当于traceroute的加强版,特别是它可以指定任意次数的探测并生成详细的探测报告,而traceroute只能对每个节点发起3次探测,数据量就不够了。 > 你也可以理解为mtr = traceroute + ping,因为ping也可以发送很多次,也有最终的报告。 比如,我们用下面这条命令,就可以对目标IP完成指定次数的探测并生成详细的报告,报告里包含了每一跳的丢包率,很有帮助。 ```bash $ mtr -c 10 -r 8.8.8.8 Start: 2022-03-27T00:44:36+0000 HOST: victorebpf                  Loss%   Snt   Last   Avg  Best  Wrst StDev   1.|-- _gateway                   0.0%    10    0.2   0.3   0.2   0.5   0.1   2.|-- 192.168.1.1                0.0%    10    1.9  14.9   1.6 119.8  37.0   3.|-- 100.65.0.1                 0.0%    10   11.9  27.8   6.6 157.0  46.6   4.|-- 61.152.53.149             20.0%    10    4.3  29.9   3.2 128.5  45.4   5.|-- 61.152.24.226             20.0%    10    4.0  10.1   3.9  44.2  13.8   6.|-- 202.97.94.237             10.0%    10   28.1  41.8  22.2 157.4  43.5   7.|-- 202.97.12.182             60.0%    10    4.9  27.9   4.9  57.5  25.7   8.|-- 202.97.93.158             10.0%    10   89.4 111.8  80.8 217.6  55.0   9.|-- 72.14.211.144             20.0%    10   79.7 107.9  79.6 231.9  51.6  10.|-- 108.170.240.225           10.0%    10   89.8  91.5  81.0 148.2  21.5  11.|-- 74.125.251.207             0.0%    10   87.7  95.4  81.0 193.3  34.5  12.|-- dns.google                10.0%    10   81.0 106.2  80.1 286.9  67.9 ``` 可见,终点处的丢包率为10.0%。不过,你也可能注意到了:为什么中间有好几个节点的丢包达到了20.0%甚至60.0%,但终点丢包率反而更低?这是怎么回事? ![图片](https://static001.geekbang.org/resource/image/f8/8c/f8555257dbfbe05d766a9199cefc2c8c.jpg?wh=629x268) ### 为何中间节点丢包率更高? 首先,终点的丢包率(在这里是10.0%)肯定是正确的,因为这就是ICMP报文到达终点后的情况。按常规的理解,中间节点的丢包率应该比终点的丢包率低或者持平,但第4跳的20.0%和第7跳的60.0%,都比终点丢包率更高,这就令人费解了。另外,Last等列的响应耗时指标,也出现了中间节点和终点倒挂的情况。 其实,这个问题需要我们结合两个知识点来理解: * **mtr探测路径节点的工作机制;** * **网络设备回复ICMP报文的工作机制。** 第一个知识点,mtr跟traceroute一样,也是通过递增TTL的方式,使得ICMP或者UDP报文终止在中间节点,从而获取到这些节点回复的ICMP time exceeded消息。也正是通过这个消息,我们知道了这个节点的IP、时延,以及我们关心的丢包率。 第二个知识点,是网络设备在回复ICMP报文时候的工作机制。其实,网络设备(交换机和路由器)在“转发”和 “回复”这两个任务上的工作机制是不同的: * 对于“转发”,大部分工作是卸载到数据面的硬件芯片完成的,这也是实现高速转发的底层基础。 * 对于“回复”,因为需要自己生成一个ICMP响应报文,那就需要动用自己的CPU资源了,速度就会慢一些。 我们可以通过下面这个图来理解这个知识点: ![](https://static001.geekbang.org/resource/image/5c/da/5cca38e739b33185140f6cca05366fda.jpg?wh=2000x1006) 另外还有一些原因会导致这个现象,比如,**不少网络设备对自己的ICMP响应报文设置了限速**(rate limit),这也会加剧这种中间节点丢包的情况。 ### 如何解读中间节点的丢包率? 那么,这个中间节点的丢包率是不是就毫无价值了呢?我们再看另外一个例子: ![图片](https://static001.geekbang.org/resource/image/55/20/5538d1517190bdf2cbe1fc73b7e97120.jpg?wh=810x271) 我们从这里可以解读出很多的信息,包括: * 从第5跳开始到最后一跳都有丢包。其中的第8跳到最后一跳的丢包率接近(都在20%左右),那么这几跳需要重点关注。 * 从第9到第11跳的节点名称解析来看,这些是日本东京(tokyjp)的路由节点,运营商是NTT。 * 第11跳的名称里带fastly,这是一个CDN厂商的名称,所以这是fastly CDN的节点。 * Last这一列是最近一次响应耗时,第7和第8跳的耗时突增了70ms。我们知道,时延跟地理距离成正比,而70ms是一个比较长的耗时了,所以我们可以判断,这两跳就是中日之间的海底光缆的两端。 根据这些信息,我们大致可以认为,丢包(本质是链路拥塞)主要发生在中日海底光缆这个位置。 当然,这是一个粗略的推断。**一般来说,mtr最好在两端都做**,然后结合两边的mtr的报告来综合分析,结论会更加准确。 为什么要在两端都做呢?这是因为,网络路由一般是“不对称的”,也就是发送是一条路径,回来往往是另外一条路径了。那么从一端发起mtr,只能看到这个方向的网络状况,另外一个方向的网络状况,就需要另外一端运行mtr来获取了。 比如我们借用一下上一讲的示意图。假设蓝色路径是起点到终点的路径,而紫色的是返回的路径,那么两条路径在r1,r3,r14这节点上是重合的,而在其他节点上“各走各的”。 ![](https://static001.geekbang.org/resource/image/f8/90/f8f50362fyyeabbb5be8c6ca056e7590.jpg?wh=2000x1125) 你看,这跟我们抓包经常要在两端同时抓,背后的逻辑是不是差不多的?大道相通,我们积累得越多,越有可能打通这些知识之间的,达成更深的掌握。 ## 丢包与MTU 在[第8讲](https://time.geekbang.org/column/article/484667)里,我们学习了MTU相关的概念。MTU是最大传输单元,而PMTU就是路径上的瓶颈MTU(最小MTU)。一旦报文设置了DF=1并超出PMTU的大小,就会被丢弃。特别是对于**相对较大尺寸的TCP报文(比如超过1400字节)总是传输失败的情况,可以优先排查PMTU**。 那么,我们有什么方法可以很方便地就找到PMTU呢? ### 快速探测PMTU的方法 其实,我们可以用一个十分简单的命令:ping。给 **ping命令加上“-s 尺寸”**这个参数,就可以发送自定义载荷尺寸的报文。比如你可以试试这个命令: ```bash ping www.baidu.com -s 1472 ``` 然后再运行: ```bash ping www.baidu.com -s 1473 ``` 看看两者的返回有没有区别? ping报文属于网络层的ICMP控制报文,所以整体的IP报文大小是载荷(1472或者1473)加上IP头部的20字节和ICMP头部的8字节。显然,指定IP报文载荷为1473后,整个IP报文就达到1501字节,正好超过了1500字节,所以 `ping www.baidu.com -s 1473` 就在发送的途中丢失,也就无法收到ICMP响应了。 不过,也许你的网络环境跟我不同,特别是如果有隧道的存在,那-s 1472也无法通过。那么你可以不断调整这个值,直到试出一个刚刚能通过的尺寸,然后在它基础上加上28字节,就是你的网络环境的PMTU了。 > 补充:这里还隐含了另外一个知识点,就是IP分片。关于它的细节,你可以回顾第8讲的内容。 ## 如何统计丢包率? 最后,我们来学习一下丢包率的统计方法。要统计丢包率,众所周知的方法应该就是用ping了,或者说“长ping”。比如下面的例子里,丢包率为1%: ```bash --- turner-tls.map.fastly.net ping statistics --- 100 packets transmitted, 99 received, 1% packet loss, time 99204ms rtt min/avg/max/mdev = 122.012/134.749/301.225/27.648 ms ``` 不过ping还是有可能无法探测出网络的真实状况。其实在上一讲里,我们就发现,如果路径中有ECMP,那么因为ECMP哈希转发策略的存在,ping的网络路径可能就跟TCP的网络路径不同。这样可能就会造成这样的状况: * 长ping没丢包的话,不等于应用也不丢包; * 长ping丢包的话,应用丢包的可能性也很大。 所以,既然ping也未必准,那**不如直接一边跑应用一边抓包,然后对抓包文件进行分析**,从而得出丢包率。而这里,又分了两种途径:图形界面和命令行。 ### 图形界面方法 在Wireshark中我们能看到TCP retransmission、Out-of-Order等信息提示,那么我们就可以借助这些信息,计算出丢包率来。 我们看一个抓包文件的Expert Information: ![图片](https://static001.geekbang.org/resource/image/d1/83/d13cd47bf1209eb8e007921b71d4fc83.jpg?wh=1804x326) 在[第4讲](https://time.geekbang.org/column/article/480068)中,我详细介绍过解读Expert Information的方法,这里就不赘述了,相信你也已经比较熟悉了。这里我们最关心的指标是retransmission,它有100个。 这些数量算多吗?没有基数就不好说了,所以让我们看一下整体包量。打开Wireshark的Statistics下拉菜单,选中第一个选项即Capture File Properties: ![图片](https://static001.geekbang.org/resource/image/74/04/74450c5ce46afe58c42f485d03ec3604.png?wh=512x162) 在弹出的界面中我们就能看到具体的数据了,比如这个抓包的整体包量是750个。 ![图片](https://static001.geekbang.org/resource/image/22/6a/22ac21a51273525d01421646037b9a6a.jpg?wh=1564x374) 然后**丢包率可以大致认为是重传率,也就是重传报文数/整体报文数**。在这里就是100/750 = 13.3%。 ### 命令行方法 另外一个办法是用 **capinfos命令**获取总的报文数: ```plain $ capinfos viaLB.pcap File name:           viaLB.pcap File type:           Wireshark/tcpdump/... - pcap File encapsulation:  Ethernet File timestamp precision:  microseconds (6) Packet size limit:   file hdr: 65535 bytes Packet size limit:   inferred: 84 bytes Number of packets:   750 File size:           68 kB Data size:           315 kB Capture duration:    2.076944 seconds First packet time:   2016-12-09 20:06:00.629223 Last packet time:    2016-12-09 20:06:02.706167 Data byte rate:      151 kBps Data bit rate:       1215 kbps Average packet size: 420.67 bytes Average packet rate: 361 packets/s SHA256:              9c34dc15bcf69b419c0e3eb0c37fa851485adbe3953018b957b14de330fa0882 RIPEMD160:           c353fdd8d634dd38ac395980b4824751f667908f SHA1:                fd86e4f6969cc8a27ff1a29cf2de88ab6d57db7d Strict time order:   True Number of interfaces in file: 1 Interface #0 info:                      Encapsulation = Ethernet (1 - ether)                      Capture length = 65535                      Time precision = microseconds (6)                      Time ticks per second = 1000000                      Number of stat entries = 0                      Number of packets = 750 ``` 结尾处就是总的报文数量,即Number of packets = 750。那么重传个数如何在命令行里获取呢? 你是否还记得之前我们学习过tshark这个命令?这里也是用 **tshark**,执行下面的命令: ```bash $ tshark -n -q -r viaLB.pcap -z "io,stat,0,tcp.analysis.retransmission" ====================================== | IO Statistics                      | |                                    | | Duration: 2.077 secs               | | Interval: 2.077 secs               | |                                    | | Col 1: tcp.analysis.retransmission | |------------------------------------| |                |1                | | | Interval       | Frames |  Bytes | | |----------------------------------| | | 0.000 <> 2.077 |    100 | 145400 | | ====================================== ``` 最后一行的Frames 100就是指重传报文个数100。同样的,把两个数字相除就得出了丢包率。 ## 丢包多少算严重? 回到重传数量占整体比例的讨论。前面的例子里丢包率是13.3%。直觉上看,这个比例也高了。那如果降低一个数量级,比如1.33%,这算高还是低呢? 这个问题可能也没有标准答案,毕竟每个应用对于丢包和重传的敏感度也有不同。而且由于公网情况复杂,本身就有一定的丢包率存在,比如像前面的海底光缆拥塞造成的丢包,也难以避免。 **实际上,公网丢包率在1%左右是一个可以接受的范围**。如果明显超过1%,比如达到了5%以上,那对应用的影响就会比较明显了,此时应该通过节点修复或者链路调整,来解决丢包的问题,把丢包率控制在1%左右,最好是1%以下。 而内网网络会比公网稳定很多。一般来说,**一个正常的内网也有万分之一左右的丢包率**。如果明显超过了这个比率,比如达到了千分之一的话,尽管依然比公网丢包率低一个数量级,但也需要认真对待并解决。 ## 小结 这节课,我们一起探讨了丢包这个关键话题的工具、原理,还有方法。我们学习了多种工具,包括: * 探测类工具:ping、mtr、traceroute、nc、telnet等。 * 统计类工具:netstat、ss、nstat。 其中,netstat和nstat都会展示 **TCP retransmission的数值,如果它随时间递增,那就说明这台主机的对外通信存在丢包的现象**。我们也了解了Linux内核本身实现了SNMP计数器,这些计数器记录了系统启动后的各个网络指标的数值,而netstat等工具正是读取内核中这些计数器,获得了相应指标的数值。 另外我们也要清楚一点,ss能提供比netstat更多的网络信息,特别是TCP socket的各种属性。这些信息对TCP方面的排查工作很有帮助,比如运行: ```bash ss -iet ``` 而对于路径排查的重要工具 **mtr** 的原理和使用方法,你也要好好掌握。在mtr的输出中,要注意中间节点的丢包率。如果终点的丢包率比前面节点的低,那前面节点的丢包率只能作为有限的参考。如果节点丢包率出现越往后越高的情况,这样的丢包率的参考价值就高很多了。 而且,最好**在通信两端都做mtr,然后结合两边的探测结果做综合分析**。 另外,在丢包这个主题中,**MTU** 也是重要角色。因为路径MTU的不一致,某些情况下就会发生超出MTU而丢包的现象。由于ICMP消息不一定会回到发送方,就会导致发送方的PMTU机制不能正常工作,也就是无法感知到这个MTU超限的事实,导致连续发送失败。 一般我们会建议对自己网络情况了解清楚后,再对主机设置合适的MTU,这样可以最大程度上确保不发生MTU超限引发问题。另外呢,我们也可以**用ping来探测PMTU**,也就是运行: ```bash ping -s 1472 #或者其他数值 ``` 最后,我们还学习了如何根据抓包文件,计算出丢包率,也就是**丢包率=重传个数/总报文数**。另外公网丢包率1%和内网丢包率万分之一,可以认为是正常范围。 ## 思考题 最后,再给你留两道思考题: * Linux中运行ifconfig -a看到的网卡接口的dropped指标,是指我们这里讨论的丢包吗?为什么? * TCP是如何在发生丢包的情况下保证传输可靠性的呢? 欢迎你在留言区分享你的答案,我们一同进步、成长。