[转帖]性能分析之TCP全连接队列占满问题分析及优化过程

news/2024/10/4 21:17:50
https://cloud.tencent.com/developer/article/1558493

 

前言

在对一个挡板系统进行测试时,遇到一个由于TCP全连接队列被占满而影响系统性能的问题,这里记录下如何进行分析及解决的。

理解下TCP建立连接过程与队列

从图中明显可以看出建立 TCP 连接的时候,有两个队列:syns queue(半连接队列)和accept queue(全连接队列),分别在第一次握手和第三次握手。 半连接队列: 保存 SYN_RECV 状态的连接。 控制参数:

  • 半连接队列的大小:min(backlog, 内核参数 net.core.somaxconn,内核参数tcp_max_syn_backlog).
  • net.ipv4.tcp_max_syn_backlog:能接受 SYN 同步包的最大客户端数量,即半连接上限;
  • tcp_syncookies:当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

accept队列-全连接队列:保存 ESTABLISHED 状态的连接。 控制参数:

  • 全连接队列的大小:min(backlog, /proc/sys/net/core/somaxconn),意思是取backlog 与 somaxconn 两值的最小值,net.core.somaxconn 定义了系统级别的全连接队列最大长度,而 backlog 只是应用层传入的参数,所以 backlog 值尽量小于net.core.somaxconn;
  • net.core.somaxconn(内核态参数,系统中每一个端口最大的监听队列的长度);
  • net.core.netdev_max_backlog(每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目);
  • ServerSocket(int port, int backlog) 代码中的backlog参数;
  • 文件句柄;
  • net.ipv4.tcp_abort_on_overflow = 0,此值为 0 表示握手到第三步时全连接队列满时则扔掉 client 发过来的 ACK,此值为 1 则说明握手到第三步时全连接队列满时则返回 reset 给客户端。

系统概况

系统的整体架构比较简单,只有一个挡板服务,业务功能主要是接受业务数据写入日志文件,并加了 35 ms的延时等待,没有复杂的运算等业务逻辑。

开始第一次压测

以 6000 线程并发,加 1 秒的等待,对挡板服务发起压力,压测结果如下: PS:应客户要求,为了模拟真实业务场景,用较大并发进行测试。

通过上图可以看出,当系统吞吐量也就是 TPS 达到 3800 左右的时候,系统开始出现部分请求失败,继续压一段时间后,报错没有减少,且有增多的趋势。这是什么原因导致的呢,接着我也观察了下,系统的资源使用情况,发现CPU也不是很高,那可以先排除系统CPU资源的问题。

这时候,我们一定要记住,当出现请求事务大量失败的时候,一定要先看以下具体的错误信息,在继续往下面分析,而不是进行盲目的猜测,这里要提一下高楼老师经常强调的证据链,一定要根据详细的错误信息指向进行下一步分析,不能根据猜测进而通过修改一些参数,或者增加系统资源来解决问题。 以下是具体的报错信息:

看到报错信息后,发现有大量的 “Connection reset” 错误,导致这种错误的原因就是服务端因为某种原因关闭了 Connection,而客户端仍然在读写数据,此时服务器会返回复位标志 “RST”,也就是刚才提到的 `“java.net.SocketException: Connection reset”。参考 Oracle 的相关文档,看到这么一段话,原文如下:

By contrast, an abortive close uses the RST (Reset) message. If either side issues an RST, this means the entire connection is aborted and the TCP stack can throw away any queued data which has not been sent or received by either application.

翻译过来也就是说:

如果任何一方发出RST,这意味着整个连接被中止,TCP栈可以丢弃任何没有被任何应用程序发送或接收的队列数据。

这样的话,问题就很明显了,接下来看下 TCP 连接队列的溢出数据统计情况,命令为:“netstat -s

代码语言:javascript
复制
# 查看TCP半连接队列溢出:
netstat -s | grep LISTEN# 查看TCPaccept队列溢出:
netstat -s | grep overflow

通过反复敲命令,可以看出这个 overflow 的值一直在增加,那么这个现象说明 server 的TCP 全连接队列的确是满了。这时候应该想到的是,全连接队列已经溢出了,下一步就应该看一下,全连接队列的占用情况,命令为:

参数说明:

  • Recv-Q:全连接当前长度
  • Send-Q:如果连接不是在建立状态,则是当前全连接最大队列长度

从上图第三列的 Send-Q 可以看出,5000 端口服务的全连接队列最大为 50,而 Recv-Q 为当前使用了多少。在压测过程中,查看指定端口的 TCP 全连接队列使用情况,如下:

上图可以看出,全连接队列几乎已经被占满,那么最终可以确定问题所在了。找到原因后,现在只要增大全连接队列的长度就可以了。 通过上面介绍的全连接队列中,我们知道全连接队列的大小为 backlog 和 somaxconn 的最小值,那么来看下 somaxconn 的取值。

可以看出 somaxconn 的值是很大的,那就只有通知开发,增加应用代码中的 backlog 的值来加大全连接队列的长度。

调大backlog值为5000后,再次进行压测

调整后的全连接队列如下图所示:

继续以 6000 并发对系统发起压力,测试结果如下:

从上面的测试结果数据看出,已经没有错误请求了,再次查看TCP全连接队列的使用情况,Recv-Q的值也变得很大,但是仍小于 5000,这也说明之前的 50 的确太小,导致全连接队列被占满,最终影响系统性能,出现大量请求失败,到此,由 TCP 连接队列满导致的问题解决。

但是仔细看上面的 JMeter 的测试结果数据,发现当系统并发达到 4600 多后,再继续加大线程,系统的响应时间开始大幅度的增加,TPS增加趋势变缓,可以看出来此时系统仍存在瓶颈。 发现仍问题后,接着往下分析。系统没有报错,响应时间变长,导致系统吞吐量增长速度变慢。这时应该清楚的是,接下来该看什么。首先查看系统CPU使用情况,发现并不是很高,说明不是系统资源不够用而引起的问题。因为挡板服务本身没有什么业务逻辑,只是加了 35 ms的延时,那么如果响应时间变慢了,那么多半是由于网络传输出现阻塞导致。 所以使用命令:

代码语言:javascript
复制
netstat -ano | grep 10.231.44.249:5000 | grep ESTABLISHED | more

看下网络队列情况: 

参数说明:

  • Send-Q:发送队列中没有被远程主机确认的 bytes 数;
  • Recv-Q:指收到的数据还在缓存中,还没被进程读取,这个值就是还没被进程读取的
  • bytes;一般是CPU处理不过来导致的。

可以看出图中标红列的数据不为 0,通过上面的解释可以判断出是系统 CPU 处理不过来了,但是CPU也没有被充分使用,那为什么会出现这种情况呢。接下我们就该看一下,CPU 在做什么。 这里使用阿里的开源工具 arthas(arthas的安装及使用这里不过多介绍了),看下挡板服务是否存在线程的资源争用或者阻塞等,发现结果如下,存在大量的线程状态为 BLOCKED 命令:thread | grep BLOCKED

命令:thread -b

看到具体的线程栈信息后,问题就比较明显了,是一个写日志的锁导致出现线程阻塞,严重影响系统的处理能力。 为了快速的验证是写日志导致的,调整日志级别为 ERROR,再次发起压力,看问题是否解决,测试结果如下:

调整日志级别后,系统的响应时间保持在 37 ms左右,吞吐量有了大幅度的提升,问题解决。

小结

通过上面的分析案例,需注意以下几点:

  1. 压测时,如果出现请求大量失败时,记住一定要先解决报错,在进行下一步的分析;
  2. 进行性能分析时,一定要找到相应的证据链一步一步的往下分析,而不是盲目的猜测,通过修改参数及加大资源配置来解决问题;
  3. 响应时间长,TPS上不去这种问题,一定要对时间进行拆分拆解,找到时间具体慢在哪里,再进行进一步的分析优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/44219.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Linux内核版本以及部分参数与性能之二

Linux内核版本以及部分参数与性能之二版本范围 grubby --set-default /boot/vmlinuz-6.9.4-1.el7.elrepo.x86_64 grubby --set-default /boot/vmlinuz-5.4.277-1.el7.elrepo.x86_64 grubby --set-default /boot/vmlinuz-4.19.12-1.el7.elrepo.x86_64 grubby --set-default /boo…

Python实用库--PrettyTable

简介 PrettyTable是一个强大的Python第三方库,专门设计用于从程序中生成美观、易于阅读的ASCII格式表格。 在数据分析、命令行工具开发、脚本编写等领域中,特别是在需要直观呈现数据而又不依赖图形界面的情况下,PrettyTable库显得尤为实用。 其设计灵感来源于PostgreSQL的ps…

随便写的小网页练习

随便写的小网页练习: 👆原视频:十分钟学会写网页【编程前端入门】 本期重做了好几遍,是全新的视角和概念,从浏览器底层渲染原理到实现网站的演示,一节课讲透,必看的一期。后面不管是写小程序还是App,都会用到这一期的概念。 这一期内容是我做完第一章个人网站后更新的,…

golang 同时(一个项目)注册http和GRPC服务

main 里面package mainimport ("fmt""github.com/gin-gonic/gin""github.com/hashicorp/consul/api"grpc "gorm_project/grpc/services""gorm_project/route""gorm_project/utils""log""net/http&…

如何设置word首页封面不出现页眉和编码

方法: 比如说我想让页码从第二页开始编号,那就双击第二页的编号,然后勾取消掉这个链接到前一页(我这里已经勾选掉了),之后直接删除掉第一页的编号就可以了如果数字不对的话,可以在“设置页码格式”中修改一下起始的页码:参考: 设置word中第一页不显示页码,第二页页码…

Paimon笔记

支持高吞吐数据摄入、变更追踪、高效分析的流批数据湖 基于LSM提升写入速度并降低写入消耗;基于有序的SortRun裁剪大部分数据以提升查询性能;支持多种merge引擎实现高性能流表打宽 基于Flink&Paimon实现,提供数据一致性管理能力,解决流式数仓普遍存在的问题 paimon通过…

6.14安卓开发日记58

实验三:Newton法程序设计 一、实验目的 掌握Hesse矩阵的计算方法和Newton法的基本思想及其迭代步骤;学会运用MATLAB编程实现常用优化算法;能够正确处理实验数据和分析实验结果及调试程序。 二、实验内容 (1)求解无约束优化问题:; (2)终止准则取; (3)完成Newton法(…

Ubuntu24.04安装金铲铲之战

结论 使用waydroid方案,可以打开游戏,实际效果后续更新参考 Documentation waydroid_script 注意 waydroid_script需要安装libndk和libhoudini