在性能测试过程中,场景会遇到一些场景,需要在各种准备条件满足的情况下才能开始进行性能测试。例如:我需要200个学生进行一个单接口的压测,首先我得让这200个学生都登录,然后才能用200个User
对象发起接口请求。
在以往的经历中,我更偏向于串行的方式去登录200个学生,然后再创建压测ThreadBase
对象进行性能测试,由于前期消耗时间比较短,也就一直没有进行优化。在学习了CyclicBarrier类在性能测试中应用之后,一直有个想法就是将这段准备时间压缩,多线程去执行,然后在某一个时刻再开始压测任务的执行。基本思路就是使用Java
多线程编程类CyclicBarrier
解决性能测试中多线程的集合点设置和多阶段性能测试问题。
这里我用了一个链路压测的脚本进行改造,原版的脚本大家可以参考:链路压测中如何记录每一个耗时的请求,如果对链路压测感兴趣的还可以参考以前的文章:
- 如何同时压测创建和删除接口
- 绑定手机号性能测试
- 手机号验证码登录性能测试
- 单点登录性能测试方案
思路
在创建ThreadBase
对象的时候,引入一个线程安全类AtomicInteger
来标记每一个线程使用的用户账号和密码,区分不同线程使用不同的用户。
集合点的设置,我设置了两处:一是执行before()
,用于等待所有线程的用户基类OkayBase
和用户业务模块类OKclass
对象初始化。二是在执行after()
方法中,用于标记一下所有线程执行完毕,这里有些多余,因为在ThreadBase
中,我设置了一个CountDownLatch
对象的属性,用于等待所有线程任务执行完毕。这里只是为了演示一下CyclicBarrier
多阶段同步的使用。
ThreadBase
类的after()
方法内容如下:
/**
* 运行待测方法后的处理
*/
protected void after() {
if (countDownLatch != null)
countDownLatch.countDown();
}
Demo实践
代码中我注释掉了实际的业务请求,只写了两个sleep()
方法和一个日志输出。
package com.okayqa.composer.performance.master1_0
import com.fun.base.constaint.ThreadLimitTimesCount
import com.fun.frame.execute.Concurrent
import com.fun.frame.httpclient.ClientManage
import com.fun.utils.ArgsUtil
import com.okayqa.composer.base.OkayBase
import com.okayqa.composer.function.OKClass
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.concurrent.CyclicBarrier
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
class BothCollectCopy extends OkayBase {
private static Logger logger = LoggerFactory.getLogger(BothCollectCopy.class)
static AtomicInteger u = new AtomicInteger(0)
static int times = 0
static int thread
static CyclicBarrier cyclicBarrier
public static void main(String[] args) {
ClientManage.init(5, 1, 0, "", 0)
def util = new ArgsUtil(args)
thread = util.getIntOrdefault(0, 5)
times = util.getIntOrdefault(1, 2)
cyclicBarrier = new CyclicBarrier(thread, new Runnable() {
@Override
void run() {
logger.info("到达人数 {} ", cyclicBarrier.getParties())
}
})
def funs = []
thread.times {
funs << new Fun()
}
new Concurrent(funs, "收藏和取消收藏").start()
allOver()
}
static int getTimes() {
return times
}
static class Fun extends ThreadLimitTimesCount {
OkayBase okayBase
OKClass driver
public Fun() {
super(null, getTimes(), null)
}
@Override
void before() {
super.before()
okayBase = getBase(u.getAndIncrement())
driver = new OKClass(okayBase)
cyclicBarrier.await(10, TimeUnit.SECONDS)
}
@Override
protected void after() {
super.after()
cyclicBarrier.await(100,TimeUnit.SECONDS)
}
@Override
protected void doing() throws Exception {
// def collect = driver.collect()
// def value = okayBase.getLastRequestId() CONNECTOR
// this.threadmark = value
// if (collect.getJSONObject("meta").getIntValue("ecode") != 0) fail(value "请求出错!")
// def collect1 = driver.unCollect()
// def value1 = okayBase.getLastRequestId()
// this.threadmark = value1
// if (collect1.getJSONObject("meta").getIntValue("ecode") != 0) fail(value1 "请求出错!")
sleep(1.0)
logger.info(this.threadName)
sleep(1.0)
}
}
}
控制台输出
代码语言:javascript复制INFO-> 当前用户:fv,IP:10.60.192.21,工作目录:/Users/fv/Documents/workspace/okay_test/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> requestid: Fdev16093173246753
INFO-> requestid: Fdev16093173245764
INFO-> requestid: Fdev16093173244862
INFO-> requestid: Fdev16093173243871
INFO-> requestid: Fdev16093173248887
INFO-> 请求uri:https://teacherpad-d***/api/t_pad/user/login,耗时:453 ms
INFO-> 请求uri:https://teacherpad***.cn/api/t_pad/user/login,耗时:433 ms
INFO-> 教师:61951375269,学科:null,名称:61951375269,登录成功!
INFO-> 教师:61951377260,学科:null,名称:61951377260,登录成功!
INFO-> 请求uri:https://teacherpad***n/api/t_pad/user/login,耗时:459 ms
INFO-> 教师:61951377259,学科:null,名称:61951377259,登录成功!
INFO-> 请求uri:https://teacherpad**cn/api/t_pad/user/login,耗时:488 ms
INFO-> 教师:61951375951,学科:null,名称:61951375951,登录成功!
INFO-> 请求uri:https://teacherpad-**cn/api/t_pad/user/login,耗时:690 ms
INFO-> 教师:61951377261,学科:null,名称:61951377261,登录成功!
INFO-> 到达人数 5
INFO-> 收藏和取消收藏0
INFO-> 收藏和取消收藏2
INFO-> 收藏和取消收藏3
INFO-> 收藏和取消收藏1
INFO-> 收藏和取消收藏4
INFO-> 收藏和取消收藏4
INFO-> 收藏和取消收藏0
INFO-> 收藏和取消收藏2
INFO-> 收藏和取消收藏1
INFO-> 收藏和取消收藏3
INFO-> 线程:收藏和取消收藏1,执行次数:2,错误次数: 0,总耗时:5 s
INFO-> 线程:收藏和取消收藏0,执行次数:2,错误次数: 0,总耗时:5 s
INFO-> 线程:收藏和取消收藏4,执行次数:2,错误次数: 0,总耗时:5 s
INFO-> 线程:收藏和取消收藏3,执行次数:2,错误次数: 0,总耗时:5 s
INFO-> 线程:收藏和取消收藏2,执行次数:2,错误次数: 0,总耗时:5 s
INFO-> 到达人数 5
INFO-> 总计5个线程,共用时:4.925 s,执行总数:10,错误数:0,失败数:0
INFO-> 数据保存成功!文件名:/Users/fv/Documents/workspace/okay_test/long/data/5收藏和取消收藏20201230163524
INFO-> 数据保存成功!文件名:/Users/fv/Documents/workspace/okay_test/long/mark/收藏和取消收藏20201230163524
INFO->
~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
> {
> ① . "rt":2006,
> ① . "total":10,
> ① . "qps":2.491528802072952,
> ① . "failRate":0.0,
> ① . "threads":5,
> ① . "startTime":"2020-12-30 16:35:24",
> ① . "endTime":"2020-12-30 16:35:28",
> ① . "errorRate":0.0,
> ① . "executeTotal":10,
> ① . "mark":"收藏和取消收藏20201230163524",
> ① . "table":""
> }
~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
INFO->
Process finished with exit code 0
- 简直完美!哈哈哈!!!