湘潭网站建设 w磐石网络,深圳网站建设 易通鼎,网站建设服务专业,怎么管理wordpress哈喽大家好#xff0c;我是咸鱼
今天分享一篇文章#xff0c;是关于 TCP 拥塞控制对数据延迟产生的影响的。作者在服务延迟变高之后进行抓包分析#xff0c;结果发现时间花在了 TCP 本身的机制上面#xff1a;客户端并不是将请求一股脑发送给服务端#xff0c;而是只发送…哈喽大家好我是咸鱼
今天分享一篇文章是关于 TCP 拥塞控制对数据延迟产生的影响的。作者在服务延迟变高之后进行抓包分析结果发现时间花在了 TCP 本身的机制上面客户端并不是将请求一股脑发送给服务端而是只发送了一部分等到接收到服务端的 ACK然后继续再发送这就造成了额外的 RTT这个额外的 RTT 是由 TCP 的拥塞控制导致的
原文链接https://www.kawabangga.com/posts/5181
这是上周在项目上遇到的一个问题在内网把问题用英文分析了一遍觉得挺有用的所以在博客上打算再写一次。
问题是这样的我们在当前的环境中网络延迟 1ms服务的延迟是 2ms现在要迁移到一个新的环境新的环境网络自身延迟(来回的延迟RTT本文中谈到延迟都指的是 RTT 延迟)是 100ms那么请问服务的延迟应该是多少
我们的预期是 102ms 左右但是现实中发现实际的延迟涨了不止 100msP99 到了 300ms 左右。
从日志中发现有请求的延迟的确很高但是模式就是 200ms, 300ms 甚至 400ms 左右看起来是多花了几个 RTT。
接下来就根据日志去抓包最后发现时间花在了 TCP 本身的机制上面这些高延迟的请求都发生在 TCP 创建连接之后。
首先是 TCP 创建连接的时间TCP 创建连接需要三次握手需要额外增加一个 RTT。为什么不是两个 RTT因为过程是这样的
0 A - B SYN
0.5RTT B - A SYNACK
1RTT A - B ACK
1RTT A - B Data即第三个包在 A 发给 B 之后A 就继续发送下面的数据了所以可以认为这第三个包不会占用额外的时间。
这样的话延迟会额外增加一个 RTT加上本身数据传输的一个 RTT那么我们能观察到的最高的 RTT 应该是 2 个 RTT即 200ms那么为什么会看到 400ms 的请求呢
从抓包分析看我发现在建立 TCP 连接之后客户端并不是将请求一股脑发送给服务端而是只发送了一部分等到接收到服务端的 ACK然后继续在发送这就造成了额外的 RTT。看到这里我恍然大悟原来是 cwnd 造成的。
cwnd 如何分析之前的博文中也提到过。简单来说这是 TCP 层面的一个机制为了避免网络赛车在建立 TCP 连接之后发送端并不知道这个网络到底能承受多大的流量所以发送端会发送一部分数据如果 OK满满加大发送数据的量。这就是 TCP 的慢启动。
那么慢启动从多少开始呢
Linux 中默认是 10.
/usr/src/linux/include/net/tcp.h:
/* TCP initial congestion window as per draft-hkchu-tcpm-initcwnd-01 */
#define TCP_INIT_CWND 10也就是说在小于 cwnd10 * MSS1448bytes 14480bytes 数据的情况下我们可以用 2 RTT 发送完毕数据。即 1 个 RTT 用于建立 TCP 连接1个 RTT 用于发送数据。
下面这个抓包可以证明这一点我在 100ms 的环境中从一端发送了正好 14480 的数据恰好是用了 200ms
100ms 用于建立连接100ms 用于发送数据
如果发送的数据小于 14480 bytes大约是 14K那么用的时间应该是一样的。
但是如果多了即使 1 byte延迟也会增加一个 RTT即需要 300ms。下面是发送 14481 bytes 的抓包情况
多出来一个 100ms 用于传输这个额外的 byte
慢启动顾名思义只发生在启动阶段如果第一波发出去的数据都能收到确认那么证明网络的容量足够可以一次性发送更多的数据这时 cwnd 就会继续增大了取决于具体拥塞控制的算法。
这就是额外的延迟的来源了。回到我们的案例这个用户的请求大约是 30K响应也大约是 30K而 cwnd 是双向的即两端分别进行慢启动所以请求发送过来 1 RTT响应 1 RTTTCP 建立连接 1 RTT加上本身数据传输就有 1 RTT总共 4RTT就解释的通了。
解决办法也很简单两个问题都可以使用 TCP 长连接来解决。
PS其实到这里读者应该发现这个服务本身的延迟在这种情况下也是 4个 RTT只不过网络环境 A 的延迟很小在 1ms 左右这样服务自己处理请求的延迟要远大于网络的延迟1 个 RTT 和 4 个 RTT 从监控上几乎看不出区别。
PPS其实以上内容比如 “慢启动顾名思义只发生在启动阶段“以及 ”两个问题都可以使用 TCP 长连接来解决“ 的表述是不准确的详见我们后面又遇到的一个问题TCP 长连接 CWND reset 的问题分析。
Initial CWND 如果修改的话也有办法。
这里的 thread 的讨论有人提出了一种方法大意是允许让应用程序通过 socket 参数来设置 CWND 的初始值
setsockopt(fd, IPPROTO_TCP, TCP_CWND, val, sizeof (val))——然后就被骂了个狗血淋头。
Stephen Hemminger 说 IETF TCP 的家伙已经觉得 Linux 里面的很多东西会允许不安全的应用了。这么做只会证明他们的想法。这个 patch 需要做很多 researech 才考虑。
如果 misuse比如应用将这个值设置的很大那么假设一种情况网络发生拥堵了这时候应用不知道网络的情况如果建立连接的话还是使用一个很大的 initcwnd 来启动会加剧拥堵情况会原来越坏永远不会自动恢复。
David Miller 的观点是应用不可能知道链路 (Route) 上的特点
initcwnd 是一个路由链路上的特点不是 by application 决定的只有人才可能清楚整个链路的质量所以这个选项只能由人 by route 设置。
所以现在只能 by route 设置。
我实验了一下将 cwnd 设置为 40:
通过 ip route 命令修改
然后在实验可以看到这时候client 发送的时候可以一次发送更多的数据了。 后记
现在看这个原因如果懂一点 TCP很快就明白其中的原理很简单。
但是现实情况是监控上只能看到 latency 升高了但是看不出具体是哪一些请求造成的只知道这个信息的话那可能的原因就很多了。到这里发现问题之后一般就进入了扯皮的阶段中间件的用户拿着监控而不是具体的请求日志去找平台平台感觉是网络问题将问题丢给网络团队网络团队去检查他们自己的监控说他们那边显示网络没有问题网络层的延迟当然没有问题。
如果要查到具体原因的话需要
先从日志中查找到具体的高延迟的请求。监控是用来发现问题的而不是用来 debug 的从日志分析时间到底花在了哪一个阶段通过抓包或者其他手段验证步骤2 这个过程略微复杂因为要从众多连接和数据包中找到具体一个 TCP 的数据流
我发现在大公司里面这个问题往往牵扯了多个团队大家在没有确认问题就出现在某一个团队负责的范围内的时候就没有人去这么查。
我在排查的时候还得到一些错误信息比如开发者告诉我 TCP 连接的保持时间是 10min然后我从日志看1min 内连续的请求依然会有高延迟的请求所以就觉得是 TCP 建立连接 overhead 之外的问题。最后抓包才发现明显的 SYN 阶段包去和开发核对逻辑才发现所谓的 10min 保持连接只是在 Server 侧一段做的Client 侧不关心这个时间会将 TCP 直接关掉。
幸好抓到的包不会骗人。