大巴

我上初中的时候,村里去县城唯一的公共交通就是一辆大巴车。「大」也说不上,应该叫中巴车。车是邻村一个叫王文义的人买的。他把车停在我们村,每天早上骑摩托车从自己的村子跑到我们村,然后以我们村为起点,一路接上邻村的人,去县城的停车场。从我们村走,可以拉更多的人。走到王文义的村子,顺路接上王文义的老婆,王文义的老婆在车上卖票,王文义开车,一张票 7 块,过年的时候一张票 10 块。

我 12 岁的时候上初中。我们村的学生大部分都去镇上的中学读,我爸重视教育,送我到县城读。每天就是坐这个中巴车去学校,在学校住 12 天,每两个星期回家一次。刚开始每天都想家,住在学校里很不习惯,12 个人一间宿舍,没有办法洗澡,宿舍臭烘烘的。周五下午离开学校,坐在车上,是心情最好的时候,因为从现在开始距离学校越来越远了。周日的下午在车上,是心情最差的时候,在家收拾好东西,上了车,就没有退路了,只有一条去学校的路。

读高中的学生也是坐这辆车,需要去县城的村民也是坐这辆车,所以每周日下午格外人多。座位上全部坐满,车的走道也是挤满了人, 挤得满满当当,超载了两倍还多。

车比较破,车内的地板是一张铁皮,有的地方还破了小洞。有一次我坐在破洞的旁边,怀着郁闷的心情,透过洞看路上的小石子向后飞去。奔驰的客车,我和路面只有一张铁皮之隔。

后来王文义换了一辆新的宇通牌客车,涂着崭新的绿色油漆,座位也没有污渍,比以前也大了,可以叫做「大巴车」了。

高中生是周日中午回学校,初中生是周一早上。冬天天短,起个大早去坐车的只有我一个初中生。有一个冬天的早晨,格外的冷,我去车站的时候还伸手不见五指。我到车站,等了一会,王文义骑着摩托车来了。还不到发车时间,他打开门让我上车等,然后去发动车子。却发现油箱被冻住了,车发动不起来。他让我在上面坐着,我从玻璃看到他从附近的人家门外扯了一把干草,点了火塞在油箱下面烤,也顺便点了一只烟。看到这我就不淡定了,赶紧下了车,站在他旁边,看着火焰在车底燃烧,想象着发生巨大爆炸,把这几吨重的铁皮炸到天上的情形。过了一会,王文义的烟抽完了,回到驾驶室尝试发动,结果还真发动着了。他下车灭了火,就起步去县城了,车上还是只有我一个人。

3 年之后,我开始读高中,还是每两个周坐他的车上学,回家。生活还是一样的麻木,每天学习超过18个小时。最喜欢周六的中午坐车回家,最讨厌周日下午坐在回县城的车上。每两周的休息时间只有不到一天。

后来我去读大学,就再也没有坐过他的车了。每次回家我坐飞机去机场,然后坐芳姐的车去滨海酒店。父亲会去滨海酒店等我,开车带我回家。

芳姐是我哥介绍给我的,任何时候需要去坐飞机,或者从机场回家,只需要在微信上和芳姐说一声,芳姐安排一辆车点对点送到机场,除了我还会顺路接上其他需要去机场的人,一个人 60,7 座车跑一趟可以赚 360。 这些人常年跑机场,从来没有误过飞机。

只是过年的时候人流量大,活也多,一次司机和我说他三天只睡了6个小时,让我听了有些害怕。

大学快毕业的时候,村里就通了公交车,去县城价格更便宜了,但是为了连接更多的村庄,公交车也绕了,原来需要 40 分钟,现在要至少一个半小时。王文义的车也再也没有出现在我们村里。

 

LRO/GRO 对于网络吞吐的影响

打开这个抓包文件,可以马上确认这是一个发送的数据比较多的连接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 国际 协议。
 

0.01% 的概率超时问题

前言:这个系列(以及我的博客)好久不更新了,原因有两个,一个是我在学习用双拼打字,手跟不上脑子,写的东西读起来不顺畅,不过现在已经复健了。双拼确实能极大地减少按键次数,在 AI 的时代,每个人需要和 AI 对话,那么怎么赶上时代的潮流,从芸芸众人脱颖而出呢?我的建议是:练习打字,打字打得快,和 AI 沟通效率高,做 AI 时代的佼佼者;第二个原因是我最近在思考人生的意义。上次录制博客 laike9m 提到了存在主义危机,第一次知道这个词,我觉得我就是陷入了存在主义危机。苦苦思索人生的意义,没有思考出什么结果。看了一些书,看的是莫言,刘震云,看了一本漫画,《我以为这辈子完蛋了- [美]艾莉·布罗什》,让我思考了很多。但是依然没有结论。人生没有思考明白,问题先来了。

有一天,我们在上线新的设备,上线之后,用户反馈他们的服务出现了网络超时的错误。超时的概率大概在 0.01%,并且出现的时间和我们上线新的设备的时间完全一致。我们把新上线的设备隔离(不再处理线上流量)用户的服务没有再出现错误了。

我们对新设备的性能非常有信心,不应该比原来的设备转发速度还低。这中间一定是有什么问题。

拓扑图简化如下:

拓扑图

其中,用户的 Client 和 Server 侧之间的网络是无法连通的,我们的网络设备会把用户的 Ethernet 包封装到 UDP 里面发送(overlay,原理就和 VPN 一样),这个设备提供了封装,转发的服务。但是用户的 Client 和 Server 感受不到中间这个 tunnel 的存在,Client 和 Server 之间的 IP 地址是可以直接 ping 通的,TCP 也是可以连通的,全靠我们的设备在中间做了转发。

我们做了一些常规检查没有发现问题,然后重新上线新的设备,要求用户在 Server 端进行抓包。得到文件如下。在一般的问题分析中,我们一般只看 packet 的 header 就够了,不需要看 application 层的 TCP payload,所以在抓包的时候我们会截断 TCP 的 payload,这样,在下载抓包文件和交流的时候,更方便一些,并不影响问题的分析。

请据此分析,造成小概率超时的问题在哪里。

如果没有头绪,请看下面的提示。

在找不到问题的时候,我们会对比正常情况下的表现,通过正常和异常的情况的不同来寻找线索。以下是原有环境的抓包文件,没有超时的请求。

对比两个抓包问题,请分析问题的根因。


==抓包破案录==

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

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

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

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

  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 国际 协议。
 

PDF 电子书重排和裁剪

很久之前画重金买的阅读器1是 A4 纸大小,无论是阅读电子书还是 paper 都很好。但是后来莫名其妙地屏幕部分区域失灵了(情况和这里2介绍的差不多),维修找不到售后,京东推给 SONY,SONY 客服根本不知道有这么个产品,所以索性换了另一款阅读器:remarkable2.

这件事也加深了我 SONY 品控差的印象,之前的买过的 SONY 产品还包括 PSV,遥感漂移了(好像 switch 也有这类问题,所以可以饶恕吧);PS4 手柄莫名其妙也坏了,PS4 主机后来也坏了。以后不想再买 SONY 的产品了……

回到 Remarkable2,这款屏幕是 10.3 英寸,没有比之前的尺寸小很多,有一些 PDF 阅读起来就不太方便。有一些阅读器支持重排版和裁剪,有这个功能就解决问题了。但是仔细一想——重排版和裁剪不应该是一个软件功能吗?那么直接使用软件对 PDF 进行处理,然后阅读处理之后的文档不是也可以解决问题吗?

然后就发现了这个软件 K2pdfopt3,可以重新版本 PDF 为阅读器的尺寸。并且可以自动删除 PDF 的白边。

比如下面这个文档,对于印刷比较友好,左侧页面有右侧的留白,右侧页面有左侧的留白,但是使用阅读器,就浪费空间了。

K2pdfopt 可以自动裁切这种空白,命令是:

k2pdfopt input.pdf -h 1872 -w 1404  -dpi 226 -p 1-50 -wrap+ -m -o output.pdf -ui- 

效果如下。

裁剪之后就没有浪费的空白页面了

双栏的论文 PDF 页可以改成单栏的:

k2pdfopt paper1.pdf -mode 2col -col 2 -n -fc- -x -y -t -ds 2  -h 1872 -w 1404 -dpi 226 -m -o output_paper.pdf
对双栏 PDF 重排

最后在阅读器上的效果如下:

  1. https://www.kawabangga.com/posts/3161 ↩︎
  2. https://www.bilibili.com/video/BV1dt411R75L/ ↩︎
  3. https://www.willus.com/k2pdfopt/ ↩︎

 

IP 网段的几种常见表示方式

IP Network

也叫做 CIDR (Classless Inter-Domain Routing),表示一个网络段,比如 192.168.0.0/24。

路由设备通过网络掩码去匹配地址,所以子网的划分一般用这种形式。/24 有的地方也用掩码 255.255.255.0,表示的内容是一样的。

ipcalc1 这个工具可以帮助计算 IP 网络。

ipcalc

IP Range

表示一个 IP 范围,从起始 IP 到结束 IP。比如 192.168.1.1 到 192.168.1.100,一共 100 个 IP。

它可以表示如:192.168.1.100 – 192.168.2.10 这种连续的段,但是 Network 是无法表示出来的。

IP Glob

使用 * 通配符来匹配 IP 的某部分,语法类似于 shell 中对文件名的 glob 匹配。

比如 192.168.1.* 就等同与 192.168.1.0/24。但是 192.168.1.2* 就没有与之等同的 Network 表示。

反过来,192.168.1.0/26 的范围是 192.168.1.0 – 192.168.1.63, 也不能用 IP Glob 表示。

SSH 的 ~/.ssh/config 就是用 IP Glob 来定义不同的 IP (Host)登陆的配置的。

IPSet

IPSet 是一个 Set,一般来说是 IP 地址和 CIDR 的集合,所以可以表示任意 IP 的集合。ACL 一般用 IPSet 的方式来配置。

  1. https://formulae.brew.sh/formula/ipcalc ↩︎