专栏文章 自研长链接压测概览

opentest-oper@360.cn · 2022年10月20日 · 8339 次阅读

一、压测背景介绍

1、项目背景
公司的长链接服务器,有以下等缺点:
● 触达率很低
● 没有点对点推送能力
● 排查问题困难
● 只支持 m2,不支持 mid
而我们商业化的项目众多,且非常依赖长链接服务,因此自研一套长连接系统提供服务,对标公司长链接服务。

二、压测分析

1.压测目的
新系统上线,确定系统的相关性能指标、性能瓶颈、服务可用性、服务稳定性等

2.压测场景
①. 新系统上线
准确探知系统能力,防止系统上线被流量打垮
②. 性能探测
探测系统中的性能瓶颈点,进行针对性优化
③. 容量规划
对站点进行精细化的容量规划,为系统扩容,性能优化提供数据参考,节省成本投入,提高资源利用率

3.压测策略
①. 基准测试 :作为基准,在后续有框架变动/代码变动/业务逻辑变动时,进行对比
②. 负载测试:确定系统的最佳负载和最大负载,合理的进行资源申请
③. 压力测试:确定系统的极限指标
④. 稳定性测试:在略低于最佳负载值下,持续压测 5~7d

4.发压策略
①. 线上单机压测时,使用 1 台发压机;超过 1000TPS 时,使用分布式发压
②. 集群压测时,使用多台机器进行分布式压测

三、压测指标预估

备注:
1.本次 RT 时间 以 TP95 为标准
2.因新业务上线,无真实流量,无法确定峰值 QPS,需要进行 TPS 估算

TPS 估算

估算 1、按照 10 亿用户,按照八二原则,计算 TPS 需要到~~46300,有 20 台机器,单机达到 2500 即可
估算 2、初次放量,按照 100w 用户 20 台机器估算,根据八二原则,单机 TPS 均值: 10 qps,峰值: 50 qps

1.业务指标

2.资源指标
● CPU 使用率 (时间):
不高于 75%-85%
● 内存 RAM(大小) 使用率:
不高于 80%
观察内存是否有尖刺或泄露
● 磁盘 I/O(速率) :
不高于 90%
● 网络 I/O(速率):
不高于 80%

3.中间件监控指标
防火墙
● 无防火墙
ngnix
● 带宽
● 配置是否正确
redis
● 缓存穿透、击穿、雪崩
mysql -- 本次压测不涉及
● 慢查询
● 线程阻塞
● 死锁
● 无索引导致全表查询

4.使用的第三方业务
● 不涉及第三方

四、压测流程

1.需求分析
熟悉需求、获取性能需求指标
①. 明确被测系统 及 压测场景
②. 明确测试内容
③. 明确测试策略
④. 明确测试指标

2.压测方案制定 与 评审
综合以上一~四的内容,加上压测机器信息,压测周知,编写压测方案,编写完成后召集相关人员进行压测方案评审,方案内容大纲如下:

备注:当服务器涉及到容器云和 HULK 时,需要提前与相关方确认,提供压测的 TPS,确认压测的执行时间,防止影响其他业务线。

3.测试用例设计与压测脚本编写
本次涉及 http 接口和 websocket 接口

Ⅰ. 压测组件选择
根据压测涉及接口 及 模拟业务场景的需要,进行最小化的组件选择。
● 测试计划
● 线程组
压测进行梯度压测,标准线程组需要频繁更改 CLI 命令,因此直接使用了并发线程组
并发线程组配置采用参数化方式,在 Linux / Docker 中执行时,可以直接使用 CLI 传入并发数,省去频繁更改脚本的麻烦
Ramp Up Time 与 Ramp Steps Count 也需要动态调整,在并发量较大时,适当调大避免曲线不正常波动带来的不准确性
●配置元件
用户自定义的变量
采用该组件记录全局参数
HTTP 信息头管理器
用于请求接口添加 Header 字段
●前置处理器
用户参数组件
模拟海量不同用户,利用 RadomString 函数,构造 m2 和 mid 。

m2   ${__RandomString(44,0123456789qwertyuiopasdfghjklzxcvbnm,)}
mid  ${__RandomString(32,0123456789qwertyuiopasdfghjklzxcvbnm,)}

该组件一定要放入并发线程组中,才能模拟每次发起请求是不同的用户
BeanShell 前置处理器
处理 HTTP 接口中字段,按照指定算法进行加密,密文作为 Websocker 的 Header 参数
● 定时器
模拟业务逻辑,构造时间间隔
● 取样器
HTTP 取样器
WebSocket 取样器
WebSocket Open Connection
WebSocket request-response Sampler
● 后置处理器
JSON 提取器
用于提取指定字段,用于传递下一接口或者做接口断言
正则表达式提取器
用于提取响应字段中的内容 url 和 port
● 断言
响应断言
用于校验接口请求是否成功
Json 断言
用于校验响应中业务逻辑是否符合预期
● 监听器
后端监听器
JMeter 抓取数据,传递给 influxdb 存储,使用 grafana 展示
● 其他组件 (调试脚本时添加,真正压测时要去掉)
查看结果树
调试取样器
汇总报告 (主要关注方差指标)
聚合报告
jp@gc - Active Threads Over Time
jp@gc - Transactions per Second
jp@gc - Response Times Over Time

Ⅱ. 压测脚本编写
脚本先在 GUL 模式下运行通过,再进行场景关联、CLI 参数化等

Ⅲ. 压测执行
①. 搭建压测环境
根据压测 TPS 不同,选择不同的压测执行环境
②. 执行测试脚本
根据压测环境的不同,压测执行方式也是不同的
windows GUI 压测执行
windows 分布式压测执行
windows CLI 压测执行
Linux CLI 压测执行
基于 Dokcer 的分布式压测执行
③. 测试结果记录
最后的压测报告生成,以及后续做对比基准使用

Ⅳ. 问题分析和调优
由于本次发现的几个问题均与性能瓶颈无关,这里就不列举了。

Ⅴ. 压测报告输出
性能测试报告是性能测试的里程碑,通过报告能展示出性能测试的最终成果,展示系统性能是否符合需求,是否有性能隐患。
注意包含以下几方面:
①. 压测背景、目的、目标
②. 参与人、时间跨度
③. 施压环境 及 施压工具
④. 数据构造方法
⑤. 压测策略
⑥. 压测执行过程记录
⑦. 记录定位瓶颈和调优过程

五、压测执行的几种方式

具体使用哪种方式,是根据压测 TPS 和申请到的资源决定的。
几种压测方式的搭建过程 以及 优缺点,会在拆解的详细章节中讲述。
本章节概览只展示下几种压测执行方式的运行方式和运行结果展示。

1.windows GUI 压测执行
● JMeter 官方提示:不要使用 GUI 模式进行负载测试!只有在测试调试或者测试用例设定或者生成的时候才用 GUI 模式。
● GUI 模式带来性能损耗,不适合 500 以上并发的压测执行
● 在 windows JMeter 中直接执行时,需要配置上以下组件,以便观察结果
查看结果树
调试取样器
汇总报告
聚合报告
jp@gc - Active Threads Over Time
jp@gc - Transactions per Second
jp@gc - Response Times Over Time

备注:JMeter 本身的问题导致结束时无法优雅关闭,忽略最后的错误即可。

2.windows 分布式压测执行 - GUI 方式
①. 原理
Jmeter 分布式测试时,客户端机器(window 系统或者 Linux 服务器)作为一个控制器 Master,控制多台 slave 机器的操作。

②. 配置
具体的 master 和 slave 的配置,会在分篇章中详细描述。不影响理解主线流程。

③. 执行
在 jmeter GUI 中可以通过如下方式调度远程 slave 机器执行压测任务
● 菜单项 - 运行 - 远程启动,指定运行机器
● 菜单项 - 运行 - 远程启动所有,会按照 remote host 中配置的负载机执行压测。

④. 查看执行结果

⑤. 清理
运行结束后需要手动去停止每个 slave 节点的进程

3.windows CLI 压测执行
CLI 相关参数:
-n 表示使用非 GUI 的方式运行
-t 表示指定 jmeter 的测试脚本
-l 表示生成指定的报告文件
-e 表示生成 html 报告
-o html 报告输出的路径
-J 相关的参数,需要结合测试脚本中定义的参数化变量使用的。

①. 本机执行脚本

jmeter -n -t cljzyycone.jmx -l jtl/x.jtl -e -o report/
-JthreadNum=10 -JrampTime=20 -JstepTime=20 -Jduration=10

查看控制台和生成的报告 - 本机执行

②. 远程调度执行脚本
master 调度 slave 执行,需要加上 -r 或者 -R 选项
调度全部 slave 使用 -r

jmeter -n -t cljzyycone.jmx -r -l jtl/x.jtl -e -o report/
-GthreadNum=10 -GrampTime=20 -GstepTime=20 -Gduration=10

调度指定 slave 使用 -R host 的形式

jmeter -n -t cljzyycone.jmx -R 10.19.1.219 -l jtl/x.jtl -e -o report/
-GthreadNum=10 -GrampTime=20 -GstepTime=20 -Gduration=5

查看控制台 - 远程调度执行

4.Linux CLI 压测执行
步骤如下:
①. Linux 上安装 JMeter
②. 创建压测目录,存放压测脚本和测试报告
③. docker-compose up 启动监控系统的几个 service
④. 执行压测脚本

#宿主机运行
jmeter -n -t cljzyycone.jmx -l jtl/cljyc.jtl -e -o report \
-JthreadNum=1000 -JrampTime=10 -JstepTime=20 -Jduration=10

⑤. 查看监控和压测报告

备注:如上的图形是有问题的,已经提交开发查看

5.基于 Dokcer 实现 JMeter 的分布式压测执行
当压测目标 TPS 较高时,需要部署多台 linux 服务器,但分布式压测要求:
● JMeter 压测要求每台机器的基础环境都要相同
每台机器都要先安装相同的 java 环境
再安装相同版本的 JMeter
配置相同 JMeter 插件
更改相同的配置文件 (slave 要改四个配置文件)
● 每台 slave 都需要单独的安装配置,如果有变动,需要按个操作一遍
● 每台 slave 需要打开指定的端口并运行 JMeter 服务器,准备就绪并等待主服务器发送指令。
● 数据驱动文件每台机器都要上传一份,且要保证路径相同,有变动的话需要全部重新上传一遍
● 执行压测时,进行统一调度比较繁琐(每台机器都要指定端口号)
使用 docker 可以很好的解决以上问题。

概述实现过程:
1.创建基于 centos7 的 java8 Dockerfile,作为 jmeter 的父层级
official 的 java8 镜像是基于 dibian 系统,在公司即使配置了国内源,仍然下载漫长又容易失败。
因此定制一个基于 centos7 的 java8 镜像,同时集成进一些常用包

2.创建 JMeter 相关的 Dockerfile,编写一些 build 命令、run 命令、exec 命令的脚本,最终启动 master 容器和 slave 容器。(后续的分章节再介绍这些脚本内容及使用方法)

3.创建 master 和 slave 镜像
以创建 slave 镜像为例

①. 编写 Dockerfile-slave

FROM handan0723/jmeter-base:v2
MAINTAINER jmeter-docker
EXPOSE 1099 50000
ENTRYPOINT $JMETER_HOME/bin/jmeter-server \
-Dserver.rmi.ssl.disable=true \
-Dserver.rmi.localport=50000 \
-Dserver_port=1099

②. 构建 slave 镜像

sh build.sh slave v2

build.sh:

#!/bin/bash

JMETER_VERSION=${JMETER_VERSION:-"5.4.3"}
IMAGE_TIMEZONE=${IMAGE_TIMEZONE:-"Asia/Shanghai"}

build_type=$1
build_tag=$2

docker build \
--build-arg JMETER_VERSION=${JMETER_VERSION} \
--build-arg TZ=${IMAGE_TIMEZONE} \
-f Dockerfile-$build_type \
-t jmeter-$build_type:$build_tag .

tip:涉及的镜像均已上传 docker hub,按需使用

[root@localhost jmeter-docker]# docker search handan0723
NAME                       DESCRIPTION                              STARS     OFFICIAL   AUTOMATED
handan0723/jmeter-base     v2版本基于handan0723 / jdk8-centos:v4,V1基…   0                    
handan0723/jmeter-master   v2                                       0                    
handan0723/jmeter-slave    v2                                       0                    
handan0723/jdk8-centos     v1-v3对应dockerfile 是使用jdk安装的,v4使用y…       0     

4.生成容器

sh runmaster.sh 1 60000
sh runslave.sh 1 1100 1099 50000 50000
sh runslave.sh 2 1101 1099 50001 50000

备注:
每个 slave 生成容器时,要更换宿主机开放给容器的端口,如 50000,50001 递增
slave 容器数据卷可以只保留 log 目录和 testdata 目录,其他非必须。log 目录用于存放日志,便于发生错误时进行问题定位;testdata 用户存放数据驱动文件。

[root@localhost jmeter-docker]# docker ps -a
CONTAINER ID   IMAGE                    COMMAND                  CREATED              STATUS              PORTS                                                                                                                             NAMES
f83d8339616f   jmeter-slave:v2          "/bin/sh -c '$JMETER…"   About a minute ago   Up About a minute   0.0.0.0:1101->1099/tcp, :::1101->1099/tcp, 0.0.0.0:50001->50000/tcp, :::50001->50000/tcp                                          jmeter-cljyc-slave-2
3e8b366dedce   jmeter-slave:v2          "/bin/sh -c '$JMETER…"   About a minute ago   Up About a minute   0.0.0.0:50000->50000/tcp, :::50000->50000/tcp, 0.0.0.0:1100->1099/tcp, :::1100->1099/tcp                                          jmeter-cljyc-slave-1
1ca4b3011cc1   prom/prometheus:latest   "/bin/prometheus --c…"   3 days ago           Up 3 days           0.0.0.0:9090->9090/tcp, :::9090->9090/tcp                                                                                         jk_prometheus
1d838551945b   google/cadvisor:latest   "/usr/bin/cadvisor -…"   3 days ago           Up 3 days           8080/tcp, 0.0.0.0:8081->8081/tcp, :::8081->8081/tcp                                                                               jk_cadvisor
31b549fc8611   grafana/grafana:latest   "/run.sh"                3 days ago           Up 3 days           0.0.0.0:3000->3000/tcp, :::3000->3000/tcp                                                                                         jk_grafana
9ce0dc4eaf22   redis:latest             "docker-entrypoint.s…"   3 days ago           Up 3 days           0.0.0.0:6380->6379/tcp, :::6380->6379/tcp                                                                                         jk_redis
e6d0f1331947   influxdb:1.8.10          "/entrypoint.sh infl…"   3 days ago           Up 3 days           0.0.0.0:8083->8083/tcp, :::8083->8083/tcp, 0.0.0.0:8086->8086/tcp, :::8086->8086/tcp, 0.0.0.0:8090->8090/tcp, :::8090->8090/tcp   jk_influxdb
39e01d546376   jmeter-master:v2         "/bin/bash"              9 days ago           Up 9 hours          0.0.0.0:60000->60000/tcp, :::60000->60000/tcp                                                                                     jmeter-cljyc-master-1

5.查看容器 ip

此处 IP 查询结果,用户 master cli 命令行中控制 slave 使用

docker inspect --format '{{ .Name }} => {{ .NetworkSettings.IPAddress }}' $( docker ps -a -q)

6.进入 master 容器

docker exec -it jmeter-cljyc-master-1 /bin/bash 

执行 CLI 命令,根据压测目标 TPS,调整阶梯数值

jmeter -n -t ./jmx/cljzyycone.jmx \
-R 172.17.0.3:1099,172.17.0.4:1099 \
-l ./jtl/cljyc.jtl \
-e -o ./report \
-j ./log/jmeter-master.log \
-GthreadNum=10 -GrampTime=10 -GstepTime=10 -Gduration=50

调度一台 slave

调度 2 台 slave

对比下执行结果,我们脚本中要求的并发数 10,不是两台共同完成 10,而是每台都按照并发 10 去执行。因此可以实现加压的初始目的。

六、监控系统

在压测过程中,通过 HTML 报告可以查阅压测结果。但是只能在压测流程结束后,才能查看指标数据。无法观测执行过程中的异常波动,不具备实时性。如果使用 Basic Graphs 插件,因 jmeter 不擅长图形绘制,这样的组件极其耗损性能,影响压测执行及压测结果的准确性。
因此需要使用监控系统,更及时准确的监控整个压测过程。
具体的组件交互原理,环境搭建方式,配置过程,将会在单独的篇章中详细列出。概览只讲述组件构成,及使用方式。

1.监控系统组成
jmeter:压测执行及数据采集
InfluxDB:分布式时序、事件和指标数据库
Prometheus:时序数据库
Grafana:开源 WEB 可视化平台

2.实现原理
采集 -- 通过 JMeter 中配置 Backend Listener 去实时采集数据
存储 -- 在 InfluxDB 中进行相关配置,存储采集来的数据
展示 -- 在 Grafana 中进行相关配置,读取 InfluxDB 数据库中的数据,配置相应的 dashboard 进行展示 JMeter 采集到的数据

3.监控系统搭建方式
windows 环境搭建 JMeter + influxdb/prometheus + grafana 监控系统,展示效果如下

基于 docker-compose 搭建 JMeter + influxdb/prometheus + grafana 监控系统

启动容器

[root@localhost jkxt]# docker-compose up -d

Creating jk_redis ... done
Creating jk_cadvisor ... done
Creating jk_prometheus ... done
Creating jk_influxdb ... 
Creating jk_redis ... 
Creating jk_cadvisor ... 
Creating jk_prometheus ... 

[root@localhost jkxt]# docker ps
CONTAINER ID   IMAGE                    COMMAND                  CREATED         STATUS         PORTS                                                                                                                             NAMES
280c447c1e97   prom/prometheus:latest   "/bin/prometheus --c…"   2 minutes ago   Up 2 minutes   0.0.0.0:9090->9090/tcp, :::9090->9090/tcp                                                                                         jk_prometheus
a00db4de47da   google/cadvisor:latest   "/usr/bin/cadvisor -…"   2 minutes ago   Up 2 minutes   8080/tcp, 0.0.0.0:8081->8081/tcp, :::8081->8081/tcp                                                                               jk_cadvisor
5c04c5ebc022   redis:latest             "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   0.0.0.0:6380->6379/tcp, :::6380->6379/tcp                                                                                         jk_redis
9edb14c81e3c   grafana/grafana:latest   "/run.sh"                2 minutes ago   Up 2 minutes   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp                                                                                         jk_grafana
c582e8e25dee   influxdb:1.8.10          "/entrypoint.sh infl…"   2 minutes ago   Up 2 minutes   0.0.0.0:8083->8083/tcp, :::8083->8083/tcp, 0.0.0.0:8086->8086/tcp, :::8086->8086/tcp, 0.0.0.0:8090->8090/tcp, :::8090->8090/tcp   jk_influxdb

配置好各个服务后,在浏览器中打开监控页面

4.监控系统为什么使用 Docke-compose
简化管理:虽然不像微服务架构动辄几十服务,监控系统涉及服务要是单独管理还是比较麻烦的,手动启停维护工作量较大
解决服务依赖性问题:监控系统服务存在依赖关系,需要相互配合才能实现监控系统的功能。
可移植性:只需要编写一份 docker-compse.yml,及提供相应服务的配置文件,可以快速移植到任何需要监控的服务器中

总结

以上内容是压测的一次概要描述,主要用于了解本次压测的背景,业务逻辑,压测指标,压测工具、压测流程 及 压测执行方式。
后续会针对具体场景进行详细实现的描述,具体拆解如下。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册