记一次典型的TCP传输吞吐效率问题

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
简介: 客户在ECS上实现了一个供小图片上传的接口,通过高防->SLB->ECS的网络链路将接口发布给终端用户,但是发现上传的速率很不理想。初看起来像是高防问题,但是通过排查最终发现这是一个典型的TCP传输吞吐量问题,并且是由于后端服务器端的配置而引起,在此记录下排查过程和相关原理。

作者:怀知

客户在ECS上实现了一个供小图片上传的接口,通过高防->SLB->ECS的网络链路将接口发布给终端用户。但是发现上传的速率很不理想,上传600K左右的小图片大约要8秒。初看起来像是高防问题,但是通过排查最终发现这是一个典型的TCP传输吞吐量问题,并且是由于后端服务器端的配置而引起,在此记录下排查过程和相关原理。

梳理和分辨问题

初看起来像是高防问题,但我们还是需要来先分辨下问题。整个传输的链路如下:

客户端 -> 4层高防节点 -> 4层SLB -> 后端RS (ECS)

测试客户端机器,SLB和后端RS都在北京,使用的是4层新高防节点(节点的地理未知不在北京)。从刚开始非常小的信息量,我们有理由怀疑因为新高防节点的引入,造成客户端到后端RS的往返RTT增加会导致上传需要更多时间。但是这个时间增加到600K需要8秒是否正常,从经验判断是不正常的,但是需要更多信息来判断问题出在哪里。

比较关心的信息如下:

这个上传时间增加问题是否是突然发生,以前的上传时间是多久?--> Answer: 这是第一次,测试就发生。

直接上传SLB是不是也比较慢?--> Answer: 看起来“不慢”。

基于上面的信息,并且确认了高防端没有明显问题,唯一能怀疑的是往返RTT的增加会导致上传需要更多时间。要继续排查下去,目前汇总起来的信息已经没有突破口。只能做更加定量地分析,也就是分别往高防和源站SLB测试上传,看需要多少时间,并且同时抓包来,验证除了RTT之外还有没有影响TCP传输效率的点。

其实上传到SLB也很慢

拿到了进一步的测试结果,大致测试结果如下:
上传文件大小605KB,上传到高防需要要大约8秒:

$ time curl -X POST https://gate.customer.com/xxx/yyy -F "expression=@/Users/customer/test.jpg"
real  0m8.067s
user  0m0.016s
sys 0m0.030s

绑host上传到SLB大约需要2.3秒:

$ time curl -X POST https://gate.customer.com/xxx/yyy -F "expression=@/Users/customer/test.jpg"
real  0m2.283s
user  0m0.017s
sys 0m0.031s

上面的定量分析明确了之前一个不太准确的信息,实际上上传到SLB的也很慢,而非之前体感的“不慢”。对于在同一城域网内,RTT时间通常小于10ms, 如果TCP窗口正常的话,客户端将605KB的图片上传到阿里云SLB,一定会是ms级别,而非秒级,2.3秒明显已经很慢了。主观感受上对2秒的体感可能还不是那么强烈,所以容易造成误判。

那么剩余的问题就是要看看为什么上传到高防和SLB都很慢,而且上传到高防更慢。这个只能从抓包里做进一步判断。

分析TCP窗口

通过抓包分析可以有效地收窄(Narrow down) 问题。直接拿到测试的抓包,能避免了很多弯路。客户端上传到高防节点的抓包如下:
image.png

可以从抓包中看到如下几个特征:

  1. 以62-64号包为例,在上传的最开始一段时间,客户端每给服务器端传输2个报文(每个报文的TCP payload大小是1466-14-40=1412字节),就需要等待服务器端的ACK,才能继续传下面两个报文。
  2. 服务器端发出的报文中的TCP接收窗口一直很小,先后只有2920和2824字节 (在上图中用红框标出)。
  3. 在75号包中,服务器端进一步将TCP接受窗口通过TCP Window Update调小,变成2824字节。之后客户端只要传输1个1466字节(TCP payload 1412字节)的报文即出现TCP Window Full,需要等服务器的ACK,再传输下面一个报文。
  4. 路径的RTT比较大,且不是很稳定。比如70号报文花费了90ms的RTT, 而61号报文只花费了31ms的RTT。
    如果比较熟悉TCP协议,那到这里基本上有结论了:服务器端的TCP接收窗口持续很小,同时加上经过高防的RTT比较大,导致TCP吞吐量很小,从而上传慢。如果不太熟悉TCP协议,那么需要解答如下几个问题。

发送端一次能传多大的在途 (in flight) 未确认数量?

TCP传输并不是发送端发送一个数据包,接收端回ACK, 发送端在继续发送下一个数据包。而是允许发送端一次发多个数据包,但是到了一定大小的数据量必须要等待ACK才能发一下批数据包,这个数据量即为:在途数据未确认数据量。

在这个案例中,很明显在途未确认数据一直很小,只有大约1-2个MSS (通常MSS是1460,下面章节会有具体介绍)大小。那么在途未确认数据量是多少呢?这取决于拥塞窗口(cwnd)和接收窗口(rwnd)的最小值。接收窗口大小每次回由对端随着ACK一起发送,而拥塞窗口则由发送端根据链路状态,通过拥塞控制和预防算法进行动态调整。

拥塞窗口

拥塞窗口是根据链路状态来动态调整的,最开始发报文给对端时,没有机会知道链路状态,所以采取比较稳健的方式将拥塞窗口初始值设置得小点,这就是TCP中的慢启动。那么设置多小呢?

RFC的推荐:

  • 4 MSS, RFC 2581 updated this value to 4 segments in April 1999;
  • 10 MSS, most recently the value was increased once more to 10 segments by RFC 6928 in April 2013.
    Linux的实现:
  • 较老版本(Linux 2.6.x) 3*MSS
  • 新版本(Linux 3.0.+) 10*MSS
    随后如果链路没有丢包,拥塞窗口的大小在慢启动中会指数增长。

接收窗口

在TCP Header中有Window字段,有16个字节。Window本身的范围可以0 ~ 64KB (65535, 2^16-1)。64KB在比较早的网络环境中被认为是一个合适的上限,而利用TCP Options的Window scale字段,这个窗口可以被扩大。比如Window scale为5,则窗口可以在Window字段的基础上放大32 (2^5)倍。

接收窗口大小每次会由对端随着ACK一起发送,我们在Wireshark里面可以看到的Window字段就是接收窗口,而非拥塞窗口。

TCP是个双工传输信道,接收窗口是有方向性的。双发各自向对端通告自己的TCP接收窗口,最终会影响对端向本端的传输效率。比如在这个案例中,客户端向服务器端上传数据,那么服务器端端通告的TCP接收窗口会影响客户端向服务端传输数据的效率。

image.png

MSS

上面每次客户端发送1466个字节(二层数据帧的总长度),取决于客户端和服务器在3次握手时所相互通告的MSS,这个字段在TCP Option中。在3次握手中,客户端通告给服务器的MSS是1460字节,服务器通告给客户端的MSS是1412字节,在传输中利用1412作为MSS来传输。所以客户端在传输报文时一个二层数据帧的大小为1412+20+20+14=1466字节。

结论

这里出现的问题的原因为:服务器端的TCP接收窗口很小,限制了在途未确认数据量一直为1 ~ 2个MSS大小。和高防和SLB本身都没有关系。

对于高防的上传报文来说,服务器端的TCP接收窗口持续很小,同时加上经过高防的RTT比较大,导致TCP吞吐量很小。对SLB的测试也能复现接收窗口小的问题,只是因为客户端到SLB是同城传输,所以RTT小很多,总用时也小很多。因为TCP接收窗口比较小,使得上传高防和上传SLB几乎和RTT呈线性关系,这个在正常的TCP传输中是几乎不可能出现的,因为正常的TCP窗口一定是在拥塞控制的过程中增大和调整的。

客户端走高防的RTT如下图:在35毫秒左右。
image.png

客户端走SLB的RTT如下图:在8毫秒左右。
image.png

解决方案

影响TCP接收窗口的因素

1. TCP receive buffer

系统层面 (net.ipv4.tcp_rmem/net.core.rmem_max/net.ipv4.tcp_adv_win_scale)

TCP接收窗口的大小在Linux系统中取决于TCP receive buffer的大小,而TCP receive buffer的大小默认由内核根据系统可用内存的情况和内核参数net.ipv4.tcp_rmem动态调节。net.ipv4.tcp_rmem在Linux 2.4中被引入,设置包括[min, default, max]。

  • min: 每个TCP socket receive buffer的最小size。默认值是4K。
  • default: TCP socket receive buffer的默认大小。这个值能够覆盖全局设置net.core.rmem_default定义的初始默认buffer size。默认值是87380字节。
  • max: 每个TCP socket receive buffer的最大size。这个值不能覆盖全局设置net.core.rmem_max。
    如下是一个内核3.10.0版本,内存8G的ECS云主机上的默认值设置:

sysctl -a | grep tcp_rmem
net.ipv4.tcp_rmem = 4096 87380 6291456

同时,不是TCP receive buffer的大小就等于TCP接收窗口的大小。有bytes/2^tcp_adv_win_scale的大小分配给应用。如果net.ipv4.tcp_adv_win_scale的大小为2,表示有1/4的TCP buffer给应用,TCP把其余的3/4给TCP接窗口。

进程设置

进程可以利用系统调用setsockopt()设置socket属性,用SO_RCVBUF参数手动设置TCP receive buffer大小。比如NGINX可以在listen中配置rcvbuf=size。

2. net.ipv4.tcp_window_scaling

在前面提到,如果要让TCP接收窗口超过64KB大小,需要利用TCP Options的Window scale字段。而在系统内核参数设置里,对应的就是net.ipv4.tcp_window_scaling参数,这个参数默认是开启的。但是在这个案例中明显不是因为net.ipv4.tcp_window_scaling的原因, TCP接收窗口的大小还远远小于64KB。

问题解决

查看了相关内核参数并没有问题,最终明确问题是因为在Web server中限制了过小的rcvbuf到导致。调整参数后上传速度明显改善。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
17天前
|
网络协议 网络安全 网络虚拟化
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算。通过这些术语的详细解释,帮助读者更好地理解和应用网络技术,应对数字化时代的挑战和机遇。
60 3
|
28天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
域名解析 缓存 网络协议
传输方式的分类【图解TCP/IP(笔记五)】
传输方式的分类【图解TCP/IP(笔记五)】
230 0
|
缓存 网络协议 算法
四十一、TCP可靠传输、流量控制、拥塞控制
四十一、TCP可靠传输、流量控制、拥塞控制
四十一、TCP可靠传输、流量控制、拥塞控制
|
缓存 网络协议 算法
计算机网络学习26:TCP/UDP对比区别、TCP流量控制、拥塞控制、超时重传时间的选择、可靠传输的实现
UDP: User Datagram Protocol 用户数据报协议 TCP: Transmission Control Protocol 传输控制协议 同时这里指的连接是指逻辑连接,而不是物理连接。
计算机网络学习26:TCP/UDP对比区别、TCP流量控制、拥塞控制、超时重传时间的选择、可靠传输的实现
|
存储 网络协议 算法
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(下)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(下)
|
网络协议 安全 机器人
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(上)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(上)
|
缓存 网络协议 安全
CentOS7下使用TCP over TLS方式安全传输远程主机系统日志
CentOS7下使用TCP over TLS方式安全传输远程主机系统日志
370 0
CentOS7下使用TCP over TLS方式安全传输远程主机系统日志
|
网络协议
TCP/UDP相关-三次握手四次挥手以及为什么三次握手-如何实现可靠UDP传输
TCP/UDP相关-三次握手四次挥手以及为什么三次握手-如何实现可靠UDP传输
153 0
|
缓存 网络协议 算法
TCP 学习笔记(三) 可靠传输
TCP 学习笔记(三) 可靠传输
TCP 学习笔记(三) 可靠传输