在性能测试过程中,场景会遇到一些场景,需要在各种准备条件满足的情况下才能开始进行性能测试。例如:我需要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)
}
}

}

控制台输出

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
  • 简直完美!哈哈哈!!!

FunTester,非著名测试开发,文章记录学习和感悟,欢迎关注,交流成长。

FunTester热文精选