惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
N
Netflix TechBlog - Medium
大猫的无限游戏
大猫的无限游戏
Cyberwarzone
Cyberwarzone
T
Troy Hunt's Blog
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
U
Unit 42
Last Week in AI
Last Week in AI
云风的 BLOG
云风的 BLOG
F
Full Disclosure
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
Hacker News - Newest:
Hacker News - Newest: "LLM"
IT之家
IT之家
Google Online Security Blog
Google Online Security Blog
Cloudbric
Cloudbric
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
罗磊的独立博客
N
News and Events Feed by Topic
S
Secure Thoughts
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
aimingoo的专栏
aimingoo的专栏
Microsoft Security Blog
Microsoft Security Blog
I
InfoQ
Hugging Face - Blog
Hugging Face - Blog
I
Intezer
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 聂微东
P
Privacy International News Feed
有赞技术团队
有赞技术团队
博客园_首页
F
Fortinet All Blogs
Recent Announcements
Recent Announcements
O
OpenAI News
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tor Project blog
B
Blog
量子位
T
Threatpost
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
CXSECURITY Database RSS Feed - CXSecurity.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
Vulnerabilities – Threatpost
酷 壳 – CoolShell
酷 壳 – CoolShell
S
SegmentFault 最新的问题
L
LangChain Blog

卡瓦邦噶!

服务器高性能网络调优 | 卡瓦邦噶! 为何写作 | 卡瓦邦噶! 读《金阁寺》 | 卡瓦邦噶! 雨季又来 | 卡瓦邦噶! MTU Probe 引起的初始延迟 | 卡瓦邦噶! 3.5 秒的固定延迟问题 | 卡瓦邦噶! 学习网络的一点经验 | 卡瓦邦噶! ARP 问题诊断 | 卡瓦邦噶! 网络断断续续…… | 卡瓦邦噶! Piccolo P2P 镜像分发 | 卡瓦邦噶! 一起看电影 | 卡瓦邦噶! 《征服C指针》 | 卡瓦邦噶! 我的姥姥 | 卡瓦邦噶! Python的哲学 Python 3.5的新特性 学校不教的计算机课 垃圾回收(GC)的三种基本方式 在编程中体验纯粹的快乐 从《美丽新世界》谈自由 在快钱实习 迷人的嗓音和迷人的故事——《Sleepyhead》 Python 的十个自然语言处理工具 记一个愚蠢的bug 一年炉石传说的游戏体验 《以撒的结合:重生》网页版图鉴 分清 C++的指针、引用和数组 笑话三则 自由比皇帝更伟大——《悲惨世界》笔记 Git 10 周年访谈:Linus 讲述背后故事 用 0x3f3f3f3f 设定最大int值的优点 Joel给计算机系学生的建议 一个词法分析器的简单实现 怎样才算健康的生活方式 MacVim 配置攻略 学习培训课程的视频效果好吗? 语言的控制 可悲的大多数 CSS样式思维导图 2014年终总结 奥巴马成为首位写程序的美国总统 青岛老城区的下水道好在哪里? 做优秀 UI 的七个建议(第二部分) 选择爱情的骑士 Git简明教程 Java集合总览 读 《1984》 java问答:终极父类(六)——等待/唤醒和接口 Java 问答:终极父类(五)——toString() Hyperlapse快速视频背后的技术细节 平庸之恶 Java程序员须知的七个日志管理工具 苏州 逛书摊随想 关于考试作弊 你的工作不仅仅是编程 推荐在线学习Java的英文资源 关注女性命运——《千禧年三部曲》 用好你的幻灯片——《演说之禅》 使用ReentrantLock和Lambda表达式让同步更纯净 新手学编程,从哪里开始? 程序员都是工程师吗? 不要学习代码,要学会思考 Java 问答:终极父类(四)——hashCode() Java 问答:终极父类(三)——finalize()和 getClass() 程序员职业之路的选择 我是一名摄影家 写给何小树的城市指南 为什么一些语言会比别的快? Java的常见误区与细节 跟朋友在一起玩游戏 死神永生——读《三体》 五种类型的程序员 创业圣经——读《黑客与画家》 纪念加西亚·马尔克斯 Junit中处理异常的另一种方式:catch-exception Java8采用Martin Fowler的方法创建内部DSL Linux HotSopt虚拟机GC线程的CPU占用率 J2EE概念介绍 如何成为一名黑客 Java 问答:终极父类(二)——equals()方法 为什么我喜欢Java Java 问答:终极父类(一)——clone()方法 七个改变世界的Java项目 java中默认类型转换的小问题 传统与创新 欢迎来到互联网 莫言和马尔克斯——读《生死疲劳》 位运算的妙用 读《人为什么活着》 写博客教会我的事情 中国特色操作系统 2013年总结 《永不妥协》影评 恨不相逢未嫁时——《廊桥遗梦》影评 如何优雅地使用PPT 给明年依然年轻的我们 天才与柱子 黑客守则和黑客精神 Looking for Freedom——《被解救的姜戈》 简洁之道
用 Wireshark 分析 TCP 吞吐瓶颈
laixintao · 2022-08-17 · via 卡瓦邦噶!

Debug 网络质量的时候,我们一般会关注两个因素:延迟和吞吐量(带宽)。延迟比较好验证,Ping 一下或者 mtr 一下就能看出来。这篇文章分享一个 debug 吞吐量的办法。

看重吞吐量的场景一般是所谓的长肥管道(Long Fat Networks, LFN, rfc7323). 比如下载大文件。吞吐量没有达到网络的上限,主要可能受 3 个方面的影响:

  1. 发送端出现了瓶颈
  2. 接收端出现了瓶颈
  3. 中间的网络层出现了瓶颈

发送端出现瓶颈一般的情况是 buffer 不够大,因为发送的过程是,应用调用 syscall,将要发送的数据放到 buffer 里面,然后由系统负责发送出去。如果 buffer 满了,那么应用会阻塞住(如果使用 block 的 API 的话),直到 buffer 可用了再继续 write,生产者和消费者模式。

发送端出现瓶颈一般都比较好排查,甚至通过应用的日志看何时阻塞住了即可。大部分情况都是第 2,3 种情况,比较难以排查。这种情况发生在,发送端的应用已经将内容写入到了系统的 buffer 中,但是系统并没有很快的发送出去。

TCP 为了优化传输效率(注意这里的传输效率,并不是单纯某一个 TCP 连接的传输效率,而是整体网络的效率),会:

  1. 保护接收端,发送的数据不会超过接收端的 buffer 大小 (Flow control)。数据发送到接受端,也是和上面介绍的过程类似,kernel 先负责收好包放到 buffer 中,然后上层应用程序处理这个 buffer 中的内容,如果接收端的 buffer 过小,那么很容易出现瓶颈,即应用程序还没来得及处理就被填满了。那么如果数据继续发过来,buffer 存不下,接收端只能丢弃。
  2. 保护网络,发送的数据不会 overwhelming 网络 (Congestion Control, 拥塞控制), 如果中间的网络出现瓶颈,会导致长肥管道的吞吐不理想;

对于接收端的保护,在两边连接建立的时候,会协商好接收端的 buffer 大小 (receiver window size, rwnd), 并且在后续的发送中,接收端也会在每一个 ack 回包中报告自己剩余和接受的 window 大小。这样,发送端在发送的时候会保证不会发送超过接收端 buffer 大小的数据。(意思是,发送端需要负责,receiver 没有 ack 的总数,不会超过 receiver 的 buffer.)

对于网络的保护,原理也是维护一个 Window,叫做 Congestion window,拥塞窗口,cwnd, 这个窗口就是当前网络的限制,发送端不会发送超过这个窗口的容量(没有 ack 的总数不会超过 cwnd)。

怎么找到这个 cwnd 的值呢?

这个就是关键了,默认的算法是 cubic, 也有其他算法可以使用,比如 Google 的 BBR.

主要的逻辑是,慢启动(Slow start), 发送数据来测试,如果能正确收到 receiver 那边的 ack,说明当前网络能容纳这个吞吐,将 cwnd x 2,然后继续测试。直到下面一种情况发生:

  1. 发送的包没有收到 ACK
  2. cwnd 已经等于 rwnd 了

第 2 点很好理解,说明网络吞吐并不是一个瓶颈,瓶颈是在接收端的 buffer 不够大。cwnd 不能超过 rwnd,不然会 overload 接收端。

对于第 1 点,本质上,发送端是用丢包来检测网络状况的,如果没有发生丢包,表示一切正常,如果发生丢包,说明网络处理不了这个发送速度,这时候发送端会直接将 cwnd 减半。

但实际造成第 1 点的情况并不一定是网络吞吐瓶颈,而可能是以下几种情况:

  1. 网络达到了瓶颈
  2. 网络质量问题丢包
  3. 中间网络设备延迟了包的送达,导致发送端没有在预期时间内收到 ACK

2 和 3 原因都会造成 cwnd 下降,无法充分利用网络吞吐。

以上就是基本的原理,下面介绍如何定位这种问题。

rwnd 查看方式

这个 window size 直接就在 TCP header 里面,抓下来就能看这个字段。

但是真正的 window size 需要乘以 factor, factor 是在 TCP 握手节点通过 TCP Options 协商的。所以如果分析一条 TCP 连接的 window size,必须抓到握手阶段的包,不然就不可以知道协商的 factor 是多少。

cwnd 查看方式

Congestion control 是发送端通过算法得到的一个动态变量,会实时调整,并不会体现在协议的传输数据中。所以要看这个,必须在发送端的机器上看。

在 Linux 中可以使用 ss -i 选项将 TCP 连接的参数都打印出来。

这里展示的单位是 TCP MSS. 即实际大小是 1460bytes * 10.

Wireshark 提供了非常实用的统计功能,可以让你一眼就能看出当前的瓶颈是发生在了哪里。但是第一次打开这个图我不会看,一脸懵逼,也没查到资料要怎么看。好在我同事会,他把我教会了,我在这里记录一下,把你也教会。

首先,打开的方式如下:

然后你会看到如下的图。

首先需要明确,tcptrace 的图表示的是单方向的数据发送,因为 tcp 是双工协议,两边都能发送数据。其中最上面写了你当前在看的图数据是从 10.0.0.1 发送到 192.168.0.1 的,然后按右下角的按钮可以切换看的方向。

X轴表示的是时间,很好理解。

然后理解一下 Y 轴表示的 Sequence Number, 就是 TCP 包中的 Sequence Number,这个很关键。图中所有的数据,都是以 Sequence Number 为准的。

所以,你如果看到如上图所示,那么说明你看反了,因为数据的 Sequence Number 并没有增加过,说明几乎没有发送过数据,需要点击 Switch Direction。

这就对了,可以看到我们传输的 Sequence Number 在随着时间增加而增加。

这里面有 3 条线,含义如下:

除此之外,另外还有两种线:

需要始终记住的是 Y 轴是 Sequence Number,红色的线表示 SACK 的线表示这一段 Sequence Number 我已经收到了,然后配合黄色线表示 ACK 过的 Sequence Number,那么发送端就会知道,在中间这段空挡,包丢了,红色线和黄色线纵向的空白,是没有被 ACK 的包。所以,需要重新传输。而蓝色的线就是表示又重新传输了一遍。

学会了看这些图,我们可以认识几种常见的 pattern:

丢包

很多红色 SACK,说明接收端那边重复在说:中间有一个包我没有收到,中间有一个包我没有收到。

吞吐受到接收端 window size 限制

从这个图可以看出,黄色的线(接收端一 ACK)一上升,蓝色就跟着上升(发送端就开始发),直到填满绿色的线(window size)。说明网络并不是瓶颈,可以调大接收端的 buffer size.

吞吐受到发送端 Buffer 的限制

为什么发送端也会限制带宽呢?如果你要榨干线路上所有的性能,那么就要了解一个概念:BDP

BDP = bandwidth * RTT

为什么这个概念很重要呢?因为 TCP 是一个可靠的协议,这就意味着它要保证发送的每一个 byte 都被 ACK,并不是发送出去就可以了。所以 sender buffer 的作用,不光是程序将要发送的内容传送给 Kernel,Kernel 要在 buffer 中存储这些数据,直到被接收端 ACK。

Buffer 需要多大才会不成为瓶颈呢?就是足够大能存放住所有未被 ACK 的数据。那么没有被 ACK 的数据最大是多大呢?其实就是 BDP。比如带宽是 10Mib/s, RTT 是 1s,那么 BDP 就是 10Mib/s * 1s = 10Mib,这个连接上最多可能有 10Mib 的数据没有被 ACK,发送端的容量必须比这个大才行(如果你要完全利用网络资源的话)。

下面是一个 Buffer 不足够大的例子:

可以看到绿线(接收端的 window size)远没有达到瓶颈,但是发送端的模式不是一直发, 而是发一段停一段。就说明发送端的 buffer 已经满了,这时候 Kernel block 住了 App,必须等这些数据被 ACK 了,才能让 App 继续往 buffer 中塞入数据。

那么怎么和下面要介绍的被 cwnd 限制了区分开呢?两种模式比较相似。

可以看一开始蓝色线的垂直距离很短,后面逐渐变长,说明 cwnd 在变大,然后变大到一定的成都不变了。说明 cwnd 没成为瓶颈。

在 Wireshark 中可以切换到 Window scaling 图。

可以发现 cwnd 并没有收缩回去。

在 window scaling 图中,绿色的是 Rcv Win, 蓝色的是 Bytes out. 蓝色线每次发送数据 burst 到某一个最高点就不再上升了。但是上升的过程也没有下降过,“没有下降过”就可以说明,cwnd 没有下降过,即 cwnd 没有成为瓶颈。

吞吐受到网络质量限制

从这张图中可以看出,接收端的 window size 远远不是瓶颈,还有很多空闲。但是发送端不会一直发直到填满接收端的 buffer。

放大可以看出,中间有很多丢包和重传,这会让发送端认为网络质量不好,会谨慎发送数据,想避免造成网络拥塞。发送端每次只发送一点点数据,发送的模式是发一点,停一点,然后再发一点,而不是一直发。这也说明很有可能是 cwnd 太小了,受到了拥塞控制算法的限制。

下面这种模式是一种更加典型的因为丢包导致带宽很小的问题:

从这个图中我们可以发现以下信息:

  1. 在这个链接中,Flow Control(即 Linux 中的 tcp buffer 参数,绿色线)远远没有达到瓶颈;
  2. 图中有很多红色线,表示 SACK,说明图中有很多丢包;
  3. 蓝色线表示发送的数据,发送的模式是,每隔 0.23s 就发送一波,然后暂停,等 0.23s 然后再发送一波。蓝色线在 Y 轴上表示一次性发送的数据,可以看到,每一段的纵向长度在不断减少。从中,我们可以得到以下信息:
    1. 0.23s 是物理上的延迟;
    2. 蓝色线没有一直发送,而是发送,暂停,发送,暂停,是因为拥塞控制算法的窗口(cwnd)变小了,每次发送很快填满窗口,等接收端(0.23s之后)收到了,再继续发送;
    3. 并且蓝色线的纵向距离每一波都在减少,说明这个窗口在每次发生丢包之后都在变小(减为一半)。

完美的 TCP 连接

最后放一张完美的 TCP 连接(长肥管道),发送端一直稳定的发,没有填满 receiver window,cwnd 也没有限制发送速率。这个完美连接的带宽是 10Mib/s,RTT < 1ms, 可以看到2s发送的 Sequence nunber 是 2500000,计算可以得到 2500000 / 1024 / 1024 * 8 / 2 = 9.535Mib/s,正好达到了带宽。

本文中用到的抓包文件可以从这里下载(credit: https://www.youtube.com/watch?v=yUmACeSmT7o):

  1. https://www.cloudshark.org/captures/f5eb7c033728
  2. https://www.cloudshark.org/captures/c967765aef38

其他的一些参考资料:

  1. https://www.stackpath.com/edge-academy/what-is-cwnd-and-rwnd/
  2. https://www.baeldung.com/cs/tcp-flow-control-vs-congestion-control
  3. https://www.cs.cornell.edu/courses/cs4450/2020sp/lecture21-congestion-control.pdf
  4. https://www.mi.fu-berlin.de/inf/groups/ag-tech/teaching/2011-12_WS/L_19531_Telematics/08_Transport_Layer.pdf
  5. https://wiki.aalto.fi/download/attachments/69901948/TCP-CongestionControlFinal.pdf
  6. https://paulgrevink.wordpress.com/2017/09/08/about-long-fat-networks-and-tcp-tuning/

帮这位老师的项目打一个广告:https://github.com/royzhr/spate 一个大流量网络性能测试工具。