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

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
S
Secure Thoughts
V2EX - 技术
V2EX - 技术
Schneier on Security
Schneier on Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
L
LangChain Blog
博客园_首页
Jina AI
Jina AI
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tenable Blog
量子位
V
V2EX
酷 壳 – CoolShell
酷 壳 – CoolShell
S
Security Affairs
Last Week in AI
Last Week in AI
Scott Helme
Scott Helme
月光博客
月光博客
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 叶小钗
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
Simon Willison's Weblog
Simon Willison's Weblog
PCI Perspectives
PCI Perspectives
人人都是产品经理
人人都是产品经理
N
News and Events Feed by Topic
腾讯CDC
P
Proofpoint News Feed
T
The Exploit Database - CXSecurity.com
J
Java Code Geeks
博客园 - 司徒正美
博客园 - Franky
Latest news
Latest news
S
SegmentFault 最新的问题
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
I
Intezer
Attack and Defense Labs
Attack and Defense Labs
H
Heimdal Security Blog
H
Hacker News: Front Page
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 【当耐特】
T
Troy Hunt's Blog
N
News | PayPal Newsroom
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Commits to openclaw:main
Recent Commits to openclaw:main
雷峰网
雷峰网

卡瓦邦噶!

服务器高性能网络调优 | 卡瓦邦噶! 为何写作 | 卡瓦邦噶! 读《金阁寺》 | 卡瓦邦噶! 雨季又来 | 卡瓦邦噶! 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——《被解救的姜戈》 简洁之道
LRO/GRO 对于网络吞吐的影响
laixintao · 2025-11-21 · via 卡瓦邦噶!

打开这个抓包文件,可以马上确认这是一个发送的数据比较多的连接1,因为 TCP sequence number 上升的很快,IP 层的包都是用最大的 MTU 发送的。

抓包文件截图

分析长肥管道,可以使用之前介绍过的技术,用 tcptrace 来分析。

打开 Statistics > TCP Stream Graphs > Time Sequence (tcptrace),可以看到下图。(如果是一个直线,说明方向看反了,点击 Switch Direction.

tcptrace 图

由于没有抓到这个 stream 的 TCP 3次卧手包,我们不知道 window scaling 是多少,所以这条绿线就可以直接忽略了。剩余的看起来一点问题没有,cwnd 打开并且保持的很好,也没有很多 SACK。在 200ms 左右有一次丢包。但是看 sequence 上涨的趋势来说,并没有造成多大的影响,很快补回来了。所以这里不是主要原因。

Sequence 上涨的趋势没有太大问题,还会有超时,那么问题就可能出在——上涨的速度不够快。同样的转发链路,我们不禁怀疑,是不是新的设备比旧的设备转发性能低?每一个包都慢几个 us,总的吞吐就低?

可以打开正常的转发抓包做对比:

转发效率高的 tcptrace

这个线确实可能更加斜一点,但是斜多少呢?我们可以看吞吐的图。

性能低的 tcpdump throughput
性能正常的 tcpdump throughput

棕色的线对应实际的传输速率(右侧的 Y 轴)。可以看到,正常情况下吞吐可以达到 220Mbps 左右,但是换上新的设备只有 140Mbps 左右。在大部分 HTTP 请求中,对于小的包,延迟的变化不会特别大,但是在长肥管道中,吞吐低就会造成传输数据就会出现差距。导致部分请求超时。

其实,新旧设备的转发速度并没有根本的区别,造成吞吐不同的原因,发生在别处。

这两幅图的对比也揭示了更加深层次的原因:即左侧的 Y 轴。

左侧 Y 轴,以及图中的蓝色点,含义是 packet 的 size 的分布,每一个点代表了一个 packet size。第一幅图中,所有的 packet 都是使用最大的 MTU 发送的。内层 overlay(VxLAN Tunnel 里面)的 MTU 是 1450.

而下图中,packet 的 size 居然超过了 MTU!

之前的一篇有关 MTU 的讨论2,我们知道,发送超过 MTU 的包是会被其他的设备丢弃的,那么为什么我们从 tcpdump 能看到超过 MTU 的包呢?这是因为网卡帮我们把收到的多个小包给合并成了一个大包,再交给操作系统(Kernel)处理,这部分现在一般是在网卡的硬件上来完成的,所以我们抓包看到的(即操作系统看到的)是网卡合并处理之后的包。这叫做 Large Receive Offload,LRO。

LRO

为什么要这么做呢?因为 CPU 是通用处理器,它能做很多事情。很忙。为了提高性能,在硬件上做的很多优化都是让其他的硬件去分担 CPU 的工作。比如:

  • 让 GPU 来代替 CPU 做矩阵运算;
  • 用专用的设备来卸载 TLS3
  • 让网卡卸载 vlan,把小包合成大包,等等;

网卡擅长做重复但是简单的事情,合并小包再是再合适不过啦!

而 CPU 的工作量主要和处理多少包有关,和包的长度关系不大,长度是 1 的包(在 kernel 里面是 skb)和长度是 10000 的包,对于 cpu 来说,只是一个 length 的 value 不同而已。包的内容是业务逻辑,主要是由应用程序处理的,在 Kernel 里面,主要关注的是包的 header。假设 CPU 的能力是每秒处理 10 万个包,如果每一个包的长度是 1Kb,那么吞吐就是 10Mbps;但如果包的平均长度是 100Kb,那么吞吐可以达到 1Gbps。所以有了网卡给我们做 LRO,就可以有效提高 CPU 的吞吐。

到现在,原因就清晰了:新设备上了之后 LRO 失效,由于服务器的网卡不再执行 LRO 功能,吞吐就下降了很多,导致了部分请求超时。

那么为什么换了新的设备之后,服务器的网卡 LRO 就失效了呢?服务器网卡 LRO 和网络设备又有什么关系?

由于做不做 LRO 是服务器的网卡的硬件实现,我们无法查看硬件的设计。但是从其他地方对于 LRO/GRO 的描述,我们可以得到一些启发。

Linux 可以在没有硬件的支持下,用软件的方式实现 Generic Receive Offload, GRO (当然了,性能肯定是要差一些)。Kernel 的文档对于 GRO 的描述4如下:

Generic receive offload is the complement to GSO. Ideally any frame assembled by GRO should be segmented to create an identical sequence of frames using GSO, and any sequence of frames segmented by GSO should be able to be reassembled back to the original by GRO. The only exception to this is IPv4 ID in the case that the DF bit is set for a given IP header. If the value of the IPv4 ID is not sequentially incrementing it will be altered so that it is when a frame assembled via GRO is segmented via GSO.

除了 GRO,还有一种机制是 GSO,即 Kernel 在发送 TCP 流的时候,无须自己把每一个 Segment 切分成符合 MTU 大小再发送,而是可以直接发送,由网卡硬件来做这个切分操作。

为了让 GRO 和 GSO 是互相可逆的,即 GRO 之后的包可以通过 GSO 还原出来。需要保证:

  • IP 包的 DF 设置为1,禁止 IP Fragmentation;
  • IP 包的 DF 如果是0,那么 IP 的 ID 必须是连续的;

两个规则只要符合一条即可。

如果 DF 为1,很好理解,GRO 和 GSO 很容易逆向出来。

如果 DF 为0,ID 连续,比如 100,101,102,那么合成一个大包,大包的 ID 是 100,也可以逆向出来。但是如果 ID 不连续,比如 101,105,107,那么合成一个大包之后,就丢失原始的信息了。

对于 VxLAN 的包,在 DPDK 的文档5中,由明确要求外层的 IP 包和内层的 IP 包都要遵守这个规则:

  • outer IPv4 ID. The IPv4 ID fields of the packets, whose DF bit in the outer IPv4 header is 0, should be increased by 1.
  • inner TCP sequence number
  • inner IPv4 ID. The IPv4 ID fields of the packets, whose DF bit in the inner IPv4 header is 0, should be increased by 1.

查看吞吐慢的 tcpdump,可以发现 outer 的 ip.df 是0,而且 ID 不连续,所以无法做 LRO/GRO。

外层 ip.id 不连续

虽然我没有想到保证可逆可以带来哪些好处,但是从网上找到的资料来看,这个是在「ip.df=0 并且 ip.id 不连续的时候,不做 GRO」唯一的理由了。在另一处的邮件讨论中6,netdev 维护者以这个原则为理由拒绝了合并。起因是 Alexander Duyck 希望添加这个 patch,以达到效果:对于 overlay 的包,GRO 不再看外层包的 ip.id ,外层可以使用 fixed header,只看内层包的 ip.id 是否连续。这样,很多(实现不正确的)网络设备也可以享受 GRO 的好处了,但是因为会打破可逆的原则,所以没有被合并。

PS:上一篇文章问题中很多读者提到 GSO,为什么是 GRO 而不是 GSO 呢?因为 9999 是 server 端口,所以 192.168.1.100:9999 是 server 端,抓包文件显示的主要流量是 client 上传给 server 的,不是 server 发给 client 的。另一个细节是,.100 发给 .200 的 delta time,一般比 .200 发给 .100 的 delta time 要低,也可以佐证 .100 是 server 端。

  1. TCP 长肥管道性能分析 ↩︎
  2. 有关 MTU 和 MSS 的一切 ↩︎
  3. https://en.wikipedia.org/wiki/TLS_acceleration ↩︎
  4. Segmentation Offloads ↩︎
  5. VxLAN GRO ↩︎
  6. [RFC,7/9] GSO: Support partial segmentation offload ↩︎

==抓包破案录==

这篇文章是抓包破案录系列文章(之前叫做《计算机网络实用技术》,后来改名了)中的一篇,这个系列正在连载中,我计划用这个系列的文章来分享一些网络抓包分析的实用技术。这些文章都是总结了我的工作经历中遇到的问题,经过精心构造和编写,每个文件附带抓包文件,通过实战来学习网络抓包与分析。

如果本文对您有帮助,欢迎扫博客右侧二维码打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!

如果您正在阅读的是题目类的文章,这个目录内容正好用来隔离其他读者的评论。读完题目可以稍作暂停,进行思考,继续向下滑动,可能会被其他的读者剧透答案。

没有链接的目录还没有写完,敬请期待……

  1. 序章
  2. 抓包技术以及技巧
  3. 理解网络的分层模型
  4. 数据是如何路由的
  5. 网络问题排查的思路和技巧
  6. 不可以用路由器?答案和解析
  7. 网工闯了什么祸?答案和解析阅读加餐!
  8. 重新认识 TCP 的握手和挥手答案和解析
  9. 3.5 秒初始延迟问题答案和解析
  10. 网络断断续续……答案和解析
  11. 延迟增加了多少?答案和解析
  12. 压测的时候 QPS 为什么上不去?答案和解析
  13. TCP 下载速度为什么这么慢?答案和解析
  14. 请求为什么超时了?答案和解析
  15. 0.01% 的概率超时问题答案和解析
  16. 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。