gitbook/网络排查案例课/docs/499777.md

494 lines
29 KiB
Markdown
Raw Permalink Normal View History

2022-09-03 22:05:03 +08:00
# 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源端口为33434TTL设置为1。这个报文经过第一个路由器时TTL会被减1也就是变为0因此包被丢弃这个路由器向源地址发回一个ICMP超时通知ICMP Time Exceeded Message于是第一跳就被探测出来了。
* 发送端把下一次发送的包的UDP源端口也加一变为33435TTL也在原来的基础加一变为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来获取了。
比如我们借用一下上一讲的示意图。假设蓝色路径是起点到终点的路径而紫色的是返回的路径那么两条路径在r1r3r14这节点上是重合的而在其他节点上“各走各的”。
![](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是如何在发生丢包的情况下保证传输可靠性的呢
欢迎你在留言区分享你的答案,我们一同进步、成长。