一次压测遇到的问题和排查过程记录

文章目录

    • 问题 & 排查
      • 1、cpu使用率过高
        • 问题描述
        • 问题排查
        • 解决方案
        • 扩展内容
      • 2、504 Gateway Time-out
        • 问题描述
        • 问题排查
        • 解决方案
      • 3、限流对压测的影响
        • 问题描述
        • 问题排查
        • 解决方案
    • jmeter相关
      • 1、beanShell 动态生成签名
      • 2、响应断言
      • 3、导出结果树请求和响应文件

问题 & 排查

1、cpu使用率过高

问题描述

压测参数:并发数 5 持续执行 300s
开始压测后,CPU开始逐步飙高,且居高不下,使用率一直增加。
在这里插入图片描述

问题排查

首先,从压测参数来看,并发数并不高,持续时间也不高,排除并发过高导致服务承载不住的可能。
其次,推测是项目中的令牌桶造成了阻塞。
最后,原因是,在获取令牌桶失败后自旋时,未进行线程状态切换让出CPU。

解决方案

添加sleep,进行线程状态切换。

扩展内容

a.线程状态切换图
在这里插入图片描述

b.线程阻塞情况
调整前:
在这里插入图片描述

调整后:
在这里插入图片描述

c. http-nio-10070-exec-1
压测时,会发现有一个线程池,名称从http-nio-10070-exec-1~http-nio-10070-exec-10。后续随着并发数的增加,该线程池中的线程数也同步增加。

问题:

  • 线程池是何时创建的
  • 线程参数是如何约定的

在tomcat-embed-core-9.0.60.jar中,org.apache.coyote.AbstractProtocol#getNameInternal().

private String getNameInternal() {
    StringBuilder name = new StringBuilder(this.getNamePrefix());
    name.append('-');
    String id = this.getId();
    if (id != null) {
        name.append(id);
    } else {
        if (this.getAddress() != null) {
            name.append(this.getAddress().getHostAddress());
            name.append('-');
        }


        int port = this.getPortWithOffset();
        if (port == 0) {
            name.append("auto-");
            name.append(this.getNameIndex());
            port = this.getLocalPort();
            if (port != -1) {
                name.append('-');
                name.append(port);
            }
        } else {
            name.append(port);
        }
    }


    return name.toString();
}

protected String getNamePrefix() {
    return this.isSSLEnabled() ? "https-" + this.getSslImplementationShortName() + "-nio" : "http-nio";
}

此时会拼接出“http-nio-10070”。

创建连接时,创建线程池。

org.apache.tomcat.util.net.AbstractEndpoint#startInternal()

public void startInternal() throws Exception {
    if (!this.running) {
        this.running = true;
        this.paused = false;
        if (this.socketProperties.getProcessorCache() != 0) {
            this.processorCache = new SynchronizedStack(128, this.socketProperties.getProcessorCache());
        }


        if (this.socketProperties.getEventCache() != 0) {
            this.eventCache = new SynchronizedStack(128, this.socketProperties.getEventCache());
        }


        if (this.socketProperties.getBufferPool() != 0) {
            this.nioChannels = new SynchronizedStack(128, this.socketProperties.getBufferPool());
        }

        //没有线程池时,开始创建
        if (this.getExecutor() == null) {
            this.createExecutor();
        }


        this.initializeConnectionLatch();
        this.poller = new NioEndpoint.Poller();
        Thread pollerThread = new Thread(this.poller, this.getName() + "-Poller");
        pollerThread.setPriority(this.threadPriority);
        pollerThread.setDaemon(true);
        pollerThread.start();
        this.startAcceptorThread();
    }


}

//创建线程池
public void createExecutor() {
    this.internalExecutor = true;
//任务队列,无上限
    TaskQueue taskqueue = new TaskQueue();
//任务线程工厂,名称添加exec,在具体创建线程时,会再拼接上线程number。
    TaskThreadFactory tf = new TaskThreadFactory(this.getName() + "-exec-", this.daemon, this.getThreadPriority());
    this.executor = new ThreadPoolExecutor(this.getMinSpareThreads(), this.getMaxThreads(), 60L, TimeUnit.SECONDS, taskqueue, tf);
    taskqueue.setParent((ThreadPoolExecutor)this.executor);
}

//任务线程工厂
public class TaskThreadFactory implements ThreadFactory {
    //线程组,默认个数是10.
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    private final boolean daemon;
    private final int threadPriority;


    public TaskThreadFactory(String namePrefix, boolean daemon, int priority) {
        SecurityManager s = System.getSecurityManager();
        this.group = s != null ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        this.namePrefix = namePrefix;
        this.daemon = daemon;
        this.threadPriority = priority;
    }


    public Thread newThread(Runnable r) {
        //线程名称拼接上线程number
        TaskThread t = new TaskThread(this.group, r, this.namePrefix + this.threadNumber.getAndIncrement());
        t.setDaemon(this.daemon);
        t.setPriority(this.threadPriority);
        if (Constants.IS_SECURITY_ENABLED) {
            PrivilegedAction<Void> pa = new PrivilegedSetTccl(t, this.getClass().getClassLoader());
            AccessController.doPrivileged(pa);
            PrivilegedAction<Void> pa = new PrivilegedSetAccessControlContext(t);
            AccessController.doPrivileged(pa);
        } else {
            t.setContextClassLoader(this.getClass().getClassLoader());
        }


        return t;
    }
}

线程池参数:

maxThreads:最大线程数 200
minSpareThreads:初始化线程数大小 10
核心线程数:min(minSpareThreads,maxThreads)

当并发数20时,可以看到http-nio线程数增加到了20。
在这里插入图片描述
当并发数调到2000时,http-nio线程数最多到了200,不会再增加了。
在这里插入图片描述

2、504 Gateway Time-out

问题描述

在jmeter查看结果树异常请求时,发现偶尔有几个报错 Gateway Time-out。

问题排查

首先排除存储过程导致的超时,因为该压测接口的测试数据是统一mock的,网络稳定的情况下,不会出现超时的情况。
其次,推测是获取令牌失败导致的超时,但在此场景下,项目中会返回“当前并发数量过高,请稍后重试”的提示语。随机抽查了一个接口的入参,根据唯一请求标识(requestId)在es中查到确实打印出了“当前并发数量过高,请稍后重试”。因此,可以确定,符合该推测,但提示语未按预期展示。

解决方案

目前网关超时统一设置6s,当获取令牌超时后,项目构造的响应体提示语“当前并发数量过高,请稍后重试”只在日志中正常打印,但网关已经返回了Gateway Time-out。

对该项目超时做额外处理(超时设置为1mins)后,此问题解决。

3、限流对压测的影响

问题描述

在并发 20 持续执行 300s时,接口rt在2s,超出预期。

问题排查

项目设置了限流,导致首次获取不到令牌桶的请求的整体耗时拉长,平均rt变长。

解决方案

调大限流允许的并发数。同样压测参数下,rt降到2s以下。

jmeter相关

1、beanShell 动态生成签名

添加前置控制器
在这里插入图片描述

编写脚本

//获取指定Param
String app_key = vars.get("app_key");  

//获取接口请求体参数
Arguments args = sampler.getArguments();

//将签名结果放到变量中
vars.put("sign", sign);

扩展内容:
a.若脚本中引入第三方jar包,需在脚本顶部import,然后添加jar。
在这里插入图片描述

b.使用变量&函数
(1)脚本中将签名结果赋给sign变量,在接口中,需要获取sign变量的值。
如:url?sign=${sign}

(2)请求体中的唯一标识outBizCode需动态改变

这里使用函数处理
${__RandomString(32,1231asada,)}

2、响应断言

响应断言,即设置如何依据响应体判断此次请求成功与否。断言的设置会影响最终异常率。
在这里插入图片描述

通用逻辑是flag=success即为响应成功,mock数据,在执行到存储过程中后会报错,但此时已经走到核心逻辑,该报错可忽略。

在这里插入图片描述

3、导出结果树请求和响应文件

页面结果树不支持搜索,因此可自定义导出的请求和响应内容,再进行搜索。

在这里插入图片描述

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

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

相关文章

域名批量查询功能常用查询方法教程

一些用户在抱怨&#xff0c;要找到好域名怎么就那么不容易呢&#xff0c;能不能让我批量查下不含0的数字啊&#xff0c;能不能查下不含4的数字啊&#xff0c;能不能查下AABBB这样的域名啊…… 别着急&#xff0c;这就给您支招啦&#xff1a;通过西部数码强大的批量查询功能&am…

活动报名|SOFA 五周年,Live Long and Prosper!

2018 年 4 月 19 日&#xff0c;我们在北京启程&#xff0c;伴随种下希望的种子&#xff0c;举办了 SOFAStack 社区的第一个开放日。转眼来到 2023 年&#xff0c;瑞兔送福又逢春暖花开&#xff0c;怀揣着新的愿景&#xff0c;我们将于 4 月 15 日回到北京庆祝 SOFA 的五岁生日…

服务器磁盘又双叒叕爆满了?被/proc占满?

又双叒叕服务器前言排查分析前言 继上一次文章&#xff1a; MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. 通过删除tomcat下的catalina.out文件&#xff0c;解决了磁盘爆满的问题后。今天又双叒叕出现这个问题。。…

万字长文解读「新一代CMDB落地的困境及出路」

2023年3月21日春分时节&#xff0c;优维结合在CMDB技术领域的经验沉淀与洞察能力&#xff0c;梳理金融客户在数据运营中面临的问题和挑战&#xff0c;为了帮助到广大客户建立健全有效的方法参考&#xff0c;全新策划了一档“CMDB数据运营精准化专场公开课”线上直播课程。该系列…

2023-Python实现百度翻译接口调用

目录 &#x1f449;1、目标网址 ​​​​​​​&#x1f449;2、接口分析调试 ​​​​​​​&#x1f449;3、python 代码实现 学习记录&#xff1a;百度翻译 ​​​​​​​&#x1f449;1、目标网址 百度翻译&#xff1a;百度翻译-200种语言互译、沟通全世界&#xff0…

【LeetCode】二叉树的中序遍历(递归,迭代,Morris遍历)

目录 题目要求&#xff1a;给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 方法一&#xff1a;递归 方法二&#xff1a;迭代 思路分析&#xff1a; 复杂度分析 代码展示&#xff1a; 方法三&#xff1a;Morris 遍历 思路分析&#xff1a; 复杂度分析…

Vue3学习笔记(9.1)

Vue.js style&#xff08;内联样式&#xff09; 我们可以在v-bind:style直接设置样式&#xff0c;可以简写:style <!--* Author: RealRoad1083425287qq.com* Date: 2023-04-02 19:41:53* LastEditors: Mei* LastEditTime: 2023-04-03 15:41:44* FilePath: \vscode\Vue3_li…

打气球游戏-第14届蓝桥杯STEMA测评Scratch真题精选

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第118讲。 蓝桥杯选拔赛现已更名为STEMA&#xff0c;即STEM 能力测试&#xff0c;是蓝桥杯大赛组委会与美国普林斯顿多…

Java包装类

包装类1. 包装类&#xff08;Wrapper&#xff09;1.1 基本数据类型和包装类之间的转换1.2 String类和包装类&#xff08;基本数据类型&#xff09;之间的转换2. 面试题3.包装类练习1. 包装类&#xff08;Wrapper&#xff09; 继承于java.lang.Number类&#xff1b;实现了java.…

java校园行为分析预警管理系统

目 录 摘 要 II ABSTRACT III 第一章 绪论 1 1.1研究背景 1 1.2选题目的 1 1.3本文研究内容 2 第二章 开发技术介绍 3 2.1开发工具介绍 3 2.2 JAVA技术介绍 3 2.3 MYSQL数据库介绍 4 第三章 系统需求分析 6 3.1可行性分析 6 3.1.1技术…

python之lambdas函数(lambda表达式)

python之lambdas函数&#xff08;lambda表达式&#xff09; lambda函数&#xff0c;也称为lambda表达式。 lambda函数&#xff08;或lambda表达式&#xff09;的语法&#xff1a; lambda arguments: expression 创建一个返回表达式值的匿名函数。其中&#xff1a; lambda 是…

Power Apps从入门到放弃教程

Power Apps从入门到放弃教程前言啥是Power apps文档资料官方文档官方公式文档官方控件文档案例实操添加数据源用户登录登录成功,跳转主界面添加组件提示语言流前言 Hello&#xff01;欢迎各位,当你选择阅读这篇文章时&#xff0c;相信你最近也在学习Power apps&#xff0c;并且…

Debian 达梦数据库 disql工具输入命令 左右移动光标乱码

Debian 达梦数据库 disql工具输入命令 左右移动光标乱码1、下载安装包rlwrap-0.46.12、编译安装rlwrap-0.46.12.1、安装依赖包2.2、编译安装2.3、安装成功3、设置rlwrap系统环境变量4、配置达梦护数据库用户环境变量5、测试效果1、下载安装包rlwrap-0.46.1 https://github.com…

K8s (一) --------- K8s 概述

目录一、kubernetes 基本介绍二、kubernetes 功能和架构1. 概述2. K8s 功能:3. 应用部署架构分类4. K8s 集群架构5. K8s 集群架构节点角色功能一、kubernetes 基本介绍 kubernetes&#xff0c;简称 K8s&#xff0c;是用 8 代替 8 个字符“ubernete”而成的缩写。是一个开源的&…

【数据结构】二叉树<遍历>

【二叉树遍历】|-前序-中序-后序-层序-|<二叉树的遍历>1.前序遍历【递归】2.中序遍历【递归】3.后序遍历【递归】4.层序遍历【非递归】4.1判断是否是完全二叉树<二叉树的遍历> 在学习二叉树遍历之前我们先了解下二叉树的概念。 二叉树是&#xff1a; 1.空树 2.非空…

最新 MySQL 8.0.32 在Win10安装部署(详细)

一、前言 MySQL官方Windows版下载地址&#xff1a;https://dev.mysql.com/downloads/installer/   本教程详细指导如何在Win10系统下安装部署最新版MySQL-8.0.32。   【MySQL系列安装部署教程】 Docker安装最新版MySQL5.7&#xff08;mysql-5.7.40&#xff09;教程&…

漫画党的福利——将图片转换成漫画风格 API,附超多免费可用API 推荐(四)

前言 今天来和大家聊聊一件非常有趣的事情——将图片转换成漫画风格的 API&#xff01;如果你是一个漫画党&#xff0c;相信这个话题一定会让你感到兴奋。通过这个 API&#xff0c;你可以将你的照片变成漫画风格&#xff0c;让它们变得更加有趣和艺术&#xff01; 我们先来看…

24《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》中文分享

​《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》 本人能力有限&#xff0c;如果错误欢迎批评指正。 第六章&#xff1a;The principles of protein folding kinetics &#xff08;蛋白质折叠动力学的原理&#xff09; -Levinthal悖论促进蛋白质折…

pyecharts实现电影数据分析可视化

根据电影数据&#xff0c;使用pyecharts进行可视化分析。 数据介绍 import pandas as pd datapd.read_csv(./电影.csv) data.head()前5行数据如下: 需要安装的python库 pip install pandas pip install pyecharts文章目录数据介绍数据清洗数据可视化上映年份及电影数量导演…

python 数据、曲线平滑处理——基于Numpy.convolve实现滑动平均滤波——详解

文章目录1 基于Numpy.convolve实现滑动平均滤波1.1 滑动平均概念1.2 滑动平均的数学原理1.3 语法1.4 滑动平均滤波示例2 曲线平滑处理——Savitzky-Golay 滤波器——详解3 基于Numpy.convolve实现滑动平均滤波——详解1 基于Numpy.convolve实现滑动平均滤波 1.1 滑动平均概念 …