依照领导要求区分了两种压测模式:固定次数压测和固定时间压测。此前一直沿用的都是固定次数,所以本次第二版剥离了固定次数的模式增加了固定时间的模式。

这是第一版:性能测试框架

第二版的threadbase代码如下:



package com.fun.base.constaint;

import com.fun.frame.SourceCode;

import java.util.concurrent.CountDownLatch;

/**
* 多线程任务基类,可单独使用
*/
public abstract class ThreadBase<T> extends SourceCode implements Runnable {

/**
* 计数锁
* <p>
* 会在concurrent类里面根据线程数自动设定
* </p>
*/
CountDownLatch countDownLatch;

/**
* 用于设置访问资源
*/
public T t;

protected ThreadBase() {
super();
}

/**
* groovy无法直接访问t,所以写了这个方法
*
* @return
*/
public String getT() {
return t.toString();
}

/**
* 运行待测方法的之前的准备
*/
protected abstract void before();

/**
* 待测方法
*
* @throws Exception
*/
protected abstract void doing() throws Exception;

/**
* 运行待测方法后的处理
*/
protected abstract void after();

public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}


}

余下是固定次数模式的压测虚拟类:


package com.fun.base.constaint;

import com.fun.frame.excute.Concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

import static com.fun.utils.Time.getTimeStamp;

/**
* 请求时间限制的多线程类,限制每个线程执行的次数
*
* <p>
* 通常在测试某项用例固定时间的场景下使用,可以提前终止测试用例
* </p>
*
* @param <T> 闭包参数传递使用,Groovy脚本会有一些兼容问题,部分对象需要tostring获取参数值
*/
public abstract class ThreadLimitTimes<T> extends ThreadBase {

private static final Logger logger = LoggerFactory.getLogger(ThreadLimitTimes.class);

/**
* 任务请求执行次数
*/
public int times;

/**
* 用于设置访问资源
*/
public T t;

public ThreadLimitTimes(T t, int times) {
this(times);
this.t = t;
}

public ThreadLimitTimes(int times) {
this();
this.times = times;
}

protected ThreadLimitTimes() {
super();
}

/**
* groovy无法直接访问t,所以写了这个方法
*
* @return
*/
public String getT() {
return t.toString();
}

@Override
public void run() {
try {
before();
List<Long> t = new ArrayList<>();
long ss = getTimeStamp();
for (int i = 0; i < times; i++) {
long s = getTimeStamp();
doing();
long e = getTimeStamp();
t.add(e - s);
}
long ee = getTimeStamp();
logger.info("执行次数:{},总耗时:{}", times, ee - ss);
Concurrent.allTimes.addAll(t);
} catch (Exception e) {
logger.warn("执行任务失败!", e);
} finally {
if (countDownLatch != null)
countDownLatch.countDown();
after();
}
}

/**
* 运行待测方法的之前的准备
*/
protected abstract void before();

/**
* 待测方法
*
* @throws Exception
*/
protected abstract void doing() throws Exception;

/**
* 运行待测方法后的处理
*/
protected abstract void after();


}

余下是固定时间模式虚拟类:


package com.fun.base.constaint;

import com.fun.frame.excute.Concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

import static com.fun.utils.Time.getTimeStamp;

/**
* 请求时间限制的多线程类,限制每个线程执行的时间
* <p>
* 通常在测试某项用例固定时间的场景下使用,可以提前终止测试用例
* </p>
*
* @param <T> 闭包参数传递使用,Groovy脚本会有一些兼容问题,部分对象需要tostring获取参数值
*/
public abstract class ThreadLimitTime<T> extends ThreadBase {

/**
* 全局的时间终止开关
*/
private static boolean key = false;

private static final Logger logger = LoggerFactory.getLogger(ThreadLimitTime.class);

/**
* 任务请求执行时间,单位是秒
*/
public int time;

/**
* 用于设置访问资源
*/
public T t;

public ThreadLimitTime(T t, int time) {
this(time);
this.t = t;
}

public ThreadLimitTime(int time) {
this();
this.time = time * 1000;
}

protected ThreadLimitTime() {
super();
}

@Override
public void run() {
try {
before();
List<Long> t = new ArrayList<>();
long ss = getTimeStamp();
while (true) {
long s = getTimeStamp();
doing();
long e = getTimeStamp();
t.add(e - s);
if ((e - ss) > time || key) break;
}
long ee = getTimeStamp();
logger.info("执行时间:{} s,总耗时:{}", time / 1000, ee - ss);
Concurrent.allTimes.addAll(t);
} catch (Exception e) {
logger.warn("执行任务失败!", e);
} finally {
if (countDownLatch != null)
countDownLatch.countDown();
after();
}
}

/**
* 用于在某些情况下提前终止测试
*/
public static void stopAllThread() {
key = true;
}


}

这里我多加了一个终止测试的key,暂时没有用,以防万一。之所以没有采用另起线程去计时原因有二:进行测试过程中无论如何都会记录时间戳,多余的计算比较时间戳大小消耗性能很低,可以忽略;另起线程设计麻烦,在发生意外情况时缺少第二种保险措施。

下面是两种实现类的Demo,以HTTPrequestbase作为基础的多线程类。下面是固定次数模式的多线程类:


/**
* http请求多线程类
*/
public class RequestThreadTimes extends ThreadLimitTimes {

static Logger logger = LoggerFactory.getLogger(RequestThreadTimes.class);

/**
* 请求
*/
public HttpRequestBase request;

/**
* 单请求多线程多次任务构造方法
*
* @param request 被执行的请求
* @param times 每个线程运行的次数
*/
public RequestThreadTimes(HttpRequestBase request, int times) {
this.request = request;
this.times = times;
}

@Override
public void before() {
GCThread.starts();
}

@Override
protected void doing() throws Exception {
getResponse(request);
}

@Override
protected void after() {
GCThread.stop();
}

/**
* 多次执行某个请求,但是不记录日志,记录方法用 loglong
* <p>此方法只适应与单个请求的重复请求,对于有业务联系的请求暂时不能适配</p>
*
* @param request 请求
* @throws IOException
*/
void getResponse(HttpRequestBase request) throws IOException {
CloseableHttpResponse response = ClientManage.httpsClient.execute(request);
String content = FanLibrary.getContent(response);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
logger.warn("响应状态码:{},响应内容:{}", content, response.getStatusLine());
response.close();
}
}

下面是固定时间模式的多线程类:


package com.fun.frame.thead;

import com.fun.base.constaint.ThreadLimitTime;
import com.fun.frame.httpclient.ClientManage;
import com.fun.frame.httpclient.FanLibrary;
import com.fun.frame.httpclient.GCThread;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
* http请求多线程类
*/
public class RequestThreadTime extends ThreadLimitTime {

static Logger logger = LoggerFactory.getLogger(RequestThreadTime.class);

/**
* 请求
*/
public HttpRequestBase request;

/**
* 单请求多线程多次任务构造方法
*
* @param request 被执行的请求
* @param times 每个线程运行的次数
*/
public RequestThreadTime(HttpRequestBase request, int time) {
this.request = request;
this.time = time;
}

@Override
public void before() {
GCThread.starts();
}

@Override
protected void doing() throws Exception {
getResponse(request);
}

@Override
protected void after() {
GCThread.stop();
}

/**
* 多次执行某个请求,但是不记录日志,记录方法用 loglong
* <p>此方法只适应与单个请求的重复请求,对于有业务联系的请求暂时不能适配</p>
*
* @param request 请求
* @throws IOException
*/
void getResponse(HttpRequestBase request) throws IOException {
CloseableHttpResponse response = ClientManage.httpsClient.execute(request);
String content = FanLibrary.getContent(response);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
logger.warn("响应状态码:{},响应内容:{}", content, response.getStatusLine());
response.close();
}
}
  • 其中可以发现,差别就在于属性time还是times的设定。

下面是使用Demo:


package com.fun;

import com.fun.base.constaint.ThreadLimitTime;
import com.fun.frame.SourceCode;
import com.fun.frame.excute.Concurrent;

import java.util.ArrayList;
import java.util.List;

public class AR extends SourceCode {


public static void main(String[] args) {

ThreadLimitTime<Object> threadLimitTime = new ThreadLimitTime<Object>(10) {
/**
* 运行待测方法的之前的准备
*/
@Override
protected void before() {

}

/**
* 待测方法
*
* @throws Exception
*/
@Override
protected void doing() throws Exception {
AR.test();
}

/**
* 运行待测方法后的处理
*/
@Override
protected void after() {
}
};
new Concurrent(threadLimitTime,5).start();

FanLibrary.testOver();
}

public static void test() {
synchronized (AR.class) {
sleep(100);
output("fun");
}
}
}

剩下的mysql和redis以及dubbo的Demo就不写了,各位看官看着发挥即可。



性能测试框架第二版_多线程