本次主要更新了标记、记录功能,以及初始化连接池的方法。具体实现请参考之前的文章:​​性能测试中标记每个请求​​​、​​如何性能测试中进行业务验证​​​、​​如何在匿名thread子类中保证线程安全​​​、- ​​性能测试中记录每一个耗时请求​​。

这两天又做了一些优化。主要方向还是将标记​​mark​​​功能放到​​threadbase​​​中,将​​mark​​​方法的参数类型由​​httprequestbase​​​变成​​threadbase​​​,把原来有​​request​​​并发实现类中实现的带有标记的​​run()​​​方法,改到有两种模式虚拟类​​ThreadLimitTimesCount​​​和​​ThreadLimitTimeCount​​​中实现,放弃了实现类中再重写​​run()​​​方法,避免了重写可能导致的BUG(的确出现了)。还有一个方向就是标记保存的优化,思路与上一个方向相同,在线程安全的情况下记录保存被标记的​​threadbase​​​内容,主要是体现在​​after()​​​方法中,避免以后的实现类保存格式不统一的问题。还有一个就是放弃了深拷贝的方式复制线程,因为坑比较大,在遇到类似​​httprequestbase​​这种不支持深拷贝对象的时候显得尤其麻烦。

下面分享代码:

package com.fun.base.constaint;

import com.fun.base.interfaces.MarkThread;
import com.fun.frame.SourceCode;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
* 多线程任务基类,可单独使用
*
* @param <T> 必需实现Serializable
*/
public abstract class ThreadBase<T> extends SourceCode implements Runnable {

public int errorNum;

public int excuteNum;

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

/**
* 标记对象
*/
public MarkThread mark;

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

protected ThreadBase() {
}

/**
* groovy无法直接访问t,所以写了这个方法
*
* @return
*/
public String getTString() {
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;
}

/**
* 拷贝对象方法,用于统计单一对象多线程调用时候的请求数和成功数,对于<T>的复杂情况,需要将T类型也重写clone方法
*
* <p>
* 此处若具体实现类而非虚拟类建议自己写clone方法
* </p>
*
* @return
*/
@Override
public ThreadBase clone() {
return deepClone(this);
}

/**
* 线程任务是否需要提前关闭,默认返回false
* <p>
* 一般用于单线程错误率过高的情况
* </p>
*
* @return
*/
public boolean status() {
return false;
}

/**
* Groovy乘法调用方法
*
* @param num
* @return
*/
public List<ThreadBase> multiply(int num) {
return range(num).mapToObj(x -> this.clone()).collect(Collectors.toList());
}


}
复制代码

下面是两种模式(定量和定时)虚拟类代码,这里只放一个定量​​ThreadLimitTimesCount​​代码:

package com.fun.base.constaint;

import com.fun.base.interfaces.MarkThread;
import com.fun.config.HttpClientConstant;
import com.fun.frame.Save;
import com.fun.frame.excute.Concurrent;
import com.fun.frame.httpclient.GCThread;
import com.fun.utils.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

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

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

public List<String> marks = new ArrayList<>();

public static Vector<String> requestMark = new Vector<>();

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

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

public ThreadLimitTimesCount(T t, int times, MarkThread markThread) {
this.times = times;
this.t = t;
this.mark = markThread;
}

protected ThreadLimitTimesCount() {
super();
}

@Override
public void run() {
try {
before();
List<Long> t = new ArrayList<>();
long ss = Time.getTimeStamp();
for (int i = 0; i < times; i++) {
try {
String m = mark == null ? EMPTY : this.mark.mark(this);
long s = Time.getTimeStamp();
doing();
long e = Time.getTimeStamp();
long diff = e - s;
t.add(diff);
if (diff > HttpClientConstant.MAX_ACCEPT_TIME) marks.add(diff + CONNECTOR + m);
excuteNum++;
if (status() || key) break;
} catch (Exception e) {
logger.warn("执行任务失败!", e);
errorNum++;
}
}
long ee = Time.getTimeStamp();
logger.info("执行次数:{},错误次数: {},总耗时:{} s", times, errorNum, (ee - ss) / 1000 + 1);
Concurrent.allTimes.addAll(t);
} catch (Exception e) {
logger.warn("执行任务失败!", e);
} finally {
if (countDownLatch != null)
countDownLatch.countDown();
after();
}

}

/**
* 运行待测方法的之前的准备
*/
@Override
public void before() {
key = false;
}

@Override
public boolean status() {
return errorNum > 10;
}

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

@Override
protected void after() {
requestMark.addAll(marks);
marks = new ArrayList<>();
GCThread.stop();
synchronized (this.getClass()) {
if (countDownLatch.getCount() == 0 && requestMark.size() != 0) {
Save.saveStringListSync(requestMark, MARK_Path.replace(LONG_Path, EMPTY) + Time.getDate().replace(SPACE_1, CONNECTOR));
requestMark = new Vector<>();
}
}
}


}
复制代码

下面是HTTP请求的实现类(定量模式):

package com.fun.frame.thead;

import com.fun.base.constaint.ThreadLimitTimesCount;
import com.fun.base.interfaces.MarkThread;
import com.fun.frame.httpclient.FanLibrary;
import com.fun.frame.httpclient.FunRequest;
import com.fun.frame.httpclient.GCThread;
import org.apache.http.client.methods.HttpRequestBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

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

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

/**
* 记录总的请求超时的情况
*/
public static Vector<String> requestMark = new Vector<>();

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


/**
* 记录当前线程超时请求
*/
public List<String> marks = new ArrayList<>();

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

/**
* 应对对每个请求进行标记的情况
*
* @param request
* @param times
* @param mark
*/
public RequestThreadTimes(HttpRequestBase request, int times, MarkThread mark) {
super(null, times, mark);
this.request = request;
}

protected RequestThreadTimes() {
super();
}

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

/**
* @throws Exception
*/
@Override
protected void doing() throws Exception {
FanLibrary.excuteSimlple(request);
}

@Override
public RequestThreadTimes clone() {
RequestThreadTimes threadTimes = new RequestThreadTimes();
threadTimes.times = this.times;
threadTimes.request = FunRequest.cloneRequest(request);
threadTimes.mark = mark.clone();
return threadTimes;
}


}
复制代码

---