You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 16 套路篇 | 如何分析常见的TCP问题
你好,我是邵亚方。
对互联网服务而言, 网络问题是非常多的而且很多问题的外在表现都是网络问题这就需要我们从网络入手分析清楚根本原因是什么。而要分析各种各样的网络问题你必须掌握一些分析手段这样在出现问题的时候你就可以高效地找到原因。这节课我就带你来了解下TCP的常见问题以及对应的分析套路。
## 在Linux上检查网络的常用工具
当服务器产生问题而我们又不清楚问题和什么有关时就需要运行一些工具来检查系统的整体状况。其中dstat是我们常用的一种检查工具
```
$ dstat
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai stl| read writ| recv send| in out | int csw
8 1 91 0 0| 0 4096B|7492B 7757B| 0 0 |4029 7399
8 1 91 0 0| 0 0 |7245B 7276B| 0 0 |4049 6967
8 1 91 0 0| 0 144k|7148B 7386B| 0 0 |3896 6971
9 2 89 0 0| 0 0 |7397B 7285B| 0 0 |4611 7426
8 1 91 0 0| 0 0 |7294B 7258B| 0 0 |3976 7062
```
如上所示dstat会显示四类系统资源的整体使用情况和两个关键的系统指标。这四类系统资源分别是CPU、磁盘I/O、 网络和内存。两个关键的系统指标是中断次数int和上下文切换次数csw。而每个系统资源又会输出它的一些关键指标这里你需要注意以下几点
![](https://static001.geekbang.org/resource/image/14/68/145508f238e794df5fbf84f200c7ce68.jpg)
如果你发现某一类系统资源对应的指标比较高你就需要进一步针对该系统资源做更深入的分析。假设你发现网络吞吐比较高那就继续观察网络的相关指标你可以用dstat -h来查看比如针对TCP就可以使用dstat -tcp
```
$ dstat --tcp
------tcp-sockets-------
lis act syn tim clo
27 38 0 0 0
27 38 0 0 0
```
它会统计并显示系统中所有的TCP连接状态这些指标的含义如下
![](https://static001.geekbang.org/resource/image/c9/a4/c91a94caf6f74b508bf3648e7e9197a4.jpg)
在得到了TCP连接的整体状况后如果你想要看TCP连接的详细信息你可以使用ss这个命令来继续观察。通过ss你可以查看到每个TCP连接都是什么样的
```
$ ss -natp
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN0 100 0.0.0.0:36457 0.0.0.0:* users:(("test",pid=11307,fd=17))
LISTEN0 5 0.0.0.0:33811 0.0.0.0:* users:(("test",pid=11307,fd=19))
ESTAB 0 0 127.0.0.1:57396 127.0.1.1:34751 users:(("test",pid=11307,fd=106))
ESTAB 0 0 127.0.0.1:57384 127.0.1.1:34751 users:(("test",pid=11307,fd=100))
```
如上所示我们能查看到每个TCP连接的状态State、接收队列大小Recv-Q、发送队列大小Send-Q、本地IP和端口Local Address:Port 、远端IP和端口Peer Address:Port以及打开该TCP连接的进程信息。
除了ss命令外你也可以使用netstat命令来查看所有TCP连接的详细信息
```
$ netstat -natp
```
不过我不建议你使用netstat最好还是用ss。因为netstat不仅比ss慢而且开销也大。netstat是通过直接读取/proc/net/下面的文件来解析网络连接信息的而ss使用的是netlink方式这种方式的效率会高很多。
netlink在解析时会依赖内核的一些诊断模块比如解析TCP信息就需要tcp\_diag这个诊断模块。如果诊断模块不存在那么ss就无法使用netlink这种方式了这个时候它就会退化到和netstat一样也就是使用解析/proc/net/这种方式,当然了,它的效率也会相应变差。
另外如果你去看netstat手册通过man netstat你会发现这样一句话“This program is obsolete. Replacement for netstat is ss”。所以以后在分析网络连接问题时我们尽量还是使用ss而不是netstat。
netstat属于net-tools这个比较古老的工具集而ss属于iproute2这个工具集。net-tools中的常用命令几乎都可以用iproute2中的新命令来代替比如
![](https://static001.geekbang.org/resource/image/ca/81/cac1d4a8592a72cd5f249449867ebb81.jpg)
除了查看系统中的网络连接信息外我们有时候还需要去查看系统的网络状态比如说系统中是否存在丢包以及是什么原因引起了丢包这时候我们就需要netstat -s或者它的替代工具nstat了
```
$ nstat -z | grep -i drop
TcpExtLockDroppedIcmps 0 0.0
TcpExtListenDrops 0 0.0
TcpExtTCPBacklogDrop 0 0.0
TcpExtPFMemallocDrop 0 0.0
TcpExtTCPMinTTLDrop 0 0.0
TcpExtTCPDeferAcceptDrop 0 0.0
TcpExtTCPReqQFullDrop 0 0.0
TcpExtTCPOFODrop 0 0.0
TcpExtTCPZeroWindowDrop 0 0.0
TcpExtTCPRcvQDrop 0 0.0
```
上面输出的这些信息就包括了常见的丢包原因因为我的这台主机很稳定所以你可以看到输出的结果都是0。
假如你通过这些常规检查手段没有发现异常那你就需要考虑使用网络分析的必备工具——tcpdump了。
## 分析网络问题你必须要掌握的工具tcpdump
tcpdump的使用技巧有很多在这里我们不讲述这些使用技巧而是讲述一下它的工作原理以便于你理解tcpdump到底在干什么以及它能够分析什么样的问题。
tcpdump的大致原理如下图所示
![](https://static001.geekbang.org/resource/image/a2/27/a2a0cdc510d8e77677ba957e0408cf27.jpg "tcpdump基本原理")
tcpdump抓包使用的是libpacp这种机制。它的大致原理是在收发包时如果该包符合tcpdump设置的规则BPF filter那么该网络包就会被拷贝一份到tcpdump的内核缓冲区然后以PACKET\_MMAP的方式将这部分内存映射到tcpdump用户空间解析后就会把这些内容给输出了。
通过上图你也可以看到在收包的时候如果网络包已经被网卡丢弃了那么tcpdump是抓不到它的在发包的时候如果网络包在协议栈里被丢弃了比如因为发送缓冲区满而被丢弃tcpdump同样抓不到它。我们可以将tcpdump的能力范围简单地总结为网卡以内的问题可以交给tcpdump来处理对于网卡以外包括网卡上的问题tcpdump可能就捉襟见肘了。这个时候你需要在对端也使用tcpdump来抓包。
你还需要知道一点那就是tcpdump的开销比较大这主要在于BPF过滤器。如果系统中存在非常多的TCP连接那么这个过滤的过程是非常耗时的所以在生产环境中要慎用。但是在出现网络问题时如果你真的没有什么排查思路那就想办法使用tcpdump来抓一下包吧也许它的输出会给你带来一些意外的惊喜。
如果生产环境上运行着很重要的业务你不敢使用tcpdump来抓包那你就得去研究一些更加轻量级的追踪方式了。接下来我给你推荐的轻量级追踪方式是TCP Tracepoints。
## TCP疑难问题的轻量级分析手段TCP Tracepoints
Tracepoint是我分析问题常用的手段之一在遇到一些疑难问题时我通常都会把一些相关的Tracepoint打开把Tracepoint输出的内容保存起来然后再在线下环境中分析。通常我会写一些Python脚本来分析这些内容毕竟Python在数据分析上还是很方便的。
对于TCP的相关问题我也习惯使用这些TCP Tracepoints来分析问题。要想使用这些Tracepoints你的内核版本需要为4.16及以上。这些常用的TCP Tracepoints路径位于/sys/kernel/debug/tracing/events/tcp/和/sys/kernel/debug/tracing/events/sock/,它们的作用如下表所示:
![](https://static001.geekbang.org/resource/image/e8/12/e8b54452ccff8545441e4b5c655b7d12.jpg)
这里多说两句表格里的tcp\_rcv\_space\_adjust是我在分析RT抖动问题时贡献给内核的具体你可以看[net: introduce a new tracepoint for tcp\_rcv\_space\_adjust](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v5.9-rc3&id=6163849d289be6ff2acd2fb520da303dec3219f0)这个commit。还有inet\_sock\_set\_state该Tracepoint也是我贡献给Linux内核的具体可详见[net: tracepoint: replace tcp\_set\_state tracepoint with inet\_sock\_set\_state tracepoint](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v5.9-rc3&id=563e0bb0dc74b3ca888e24f8c08f0239fe4016b0)。其实我对“inet\_sock\_set\_state”这个名字不太满意本来想使用“inet\_sk\_set\_state”来命名的因为后者更精炼但是为了更好地匹配内核里的struct inet\_sock结构体我还是选择了现在这个略显臃肿的名字。
我们回到TCP Tracepoints这一轻量级的追踪方式。有一篇文章对它讲解得很好就是Brendan Gregg写的[TCP Tracepoints](http://www.brendangregg.com/blog/2018-03-22/tcp-tracepoints.html)这里面还详细介绍了基于Tracepoints的一些工具如果你觉得用Python脚本解析TCP Tracepoints的输出有点麻烦你可以直接使用里面推荐的那些工具。不过你需要注意的是这些工具都是基于ebpf来实现的而ebpf有一个缺点就是它在加载的时候CPU开销有些大。这是因为有一些编译工作比较消耗CPU所以你在使用这些命令时要先看下你的系统CPU使用情况。当ebpf加载起来后CPU开销就很小了大致在1%以内。在停止ebpf工具的追踪时也会有一些CPU开销不过这个开销比加载时消耗的要小很多但是你同样需要注意一下以免影响到业务。
相比于tcpdump的臃肿这些TCP Tracepoints就很轻量级了你有必要用一用它们。
好了, 我们这节课就讲到这里。
## 课堂总结
我们讲了TCP问题分析的惯用套路我再次强调一下这节课的重点
* 尽量不要使用netstat命令而是多使用它的替代品ss因为ss的性能开销更小运行也更快
* 当你面对网络问题一筹莫展时可以考虑使用tcpdump抓包看看当系统中的网络连接数较大时它对系统性能会产生比较明显的影响所以你需要想办法避免它给业务带来实质影响
* TCP Tracepoints是比较轻量级的分析方案你需要去了解它们最好试着去用一下它们。
## 课后作业
请问tcpdump在解析内核缓冲区里的数据时为什么使用PACKET\_MMAP这种方式你了解这种方式吗这样做的好处是什么欢迎你在留言区与我讨论。
感谢你的阅读,如果你认为这节课的内容有收获,也欢迎把它分享给你的朋友,我们下一讲见。