在做性能测试过程中,遇到一个棘手的问题,开发让我们复现几个请求时间较长的请求,他们看日志进行链路追踪,查找瓶颈所在。
这里说一下框架中的处理逻辑:每个请求有一个唯一的requestid,由几部分组成,还有一些算法保证其唯一性。然后这个requestID贯穿整个请求过程的日志,服务间的相互调用,与数据库中间件的交互都依赖于这个requestID。
以往压测都是写了一个请求ID,并未对这个header做处理,现在得搞起来了。
首先我先新建了一个Java interface,用于使用闭包直接完成这个功能,还有就是其他标记方法:
package com.fun.base.interfaces;
import org.apache.http.client.methods.HttpRequestBase;
import java.io.Serializable;
/**
* 用来标记request,为了记录超时的请求
*/
public interface MarkRequest extends Serializable {
/**
* 用来标记base,删除header其中一项,添加一项
*
* @param base
* @return
*/
public String mark(HttpRequestBase base);
}
然后我再 ThreadLimitTimesCount 和 ThreadLimitTimeCount 实现类中使用这个接口对象,两个实现类的代码已经发过了 性能测试框架第二版 ,这里只写一个:
中间用到了深拷贝的方法,在之前也记录过了 拷贝HttpRequestBase对象 ,目前是每一个线程对应一个 mark 对象,而不是多线程共享,下面会看到效果。
package com.fun.frame.thead;
import com.fun.base.constaint.ThreadLimitTimesCount;
import com.fun.base.interfaces.MarkRequest;
import com.fun.config.Constant;
import com.fun.config.HttpClientConstant;
import com.fun.frame.Save;
import com.fun.frame.excute.Concurrent;
import com.fun.frame.httpclient.FanLibrary;
import com.fun.frame.httpclient.FunRequest;
import com.fun.frame.httpclient.GCThread;
import com.fun.utils.Time;
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 {
private static final long serialVersionUID = -2751325651625435070L;
static Logger logger = LoggerFactory.getLogger(RequestThreadTimes.class);
/**
* 记录总的请求超时的情况
*/
public static Vector<String> requestMark = new Vector<>();
/**
* 请求
*/
public HttpRequestBase request;
/**
* 标记对象
*/
public MarkRequest mark;
/**
* 记录当前线程超时请求
*/
public List<String> marks = new ArrayList<>();
/**
* 单请求多线程多次任务构造方法
*
* @param request 被执行的请求
* @param times 每个线程运行的次数
*/
public RequestThreadTimes(HttpRequestBase request, int times) {
this.request = request;
this.times = times;
this.mark = new MarkRequest() {
private static final long serialVersionUID = 5599842482575655279L;
@Override
public String mark(HttpRequestBase base) {
return EMPTY;
}
};
}
/**
* 应对对每个请求进行标记的情况
*
* @param request
* @param times
* @param mark
*/
public RequestThreadTimes(HttpRequestBase request, int times, MarkRequest mark) {
this(request, times);
this.mark = mark;
}
protected RequestThreadTimes() {
super();
}
@Override
public void before() {
super.before();
GCThread.starts();
}
/**
* @throws Exception
*/
@Override
protected void doing() throws Exception {
FanLibrary.excuteSimlple(request);
}
@Override
protected void after() {
requestMark.addAll(marks);
GCThread.stop();
synchronized (RequestThreadTimes.class) {
if (countDownLatch.getCount() == 0) Save.saveStringList(requestMark, Constant.DEFAULT_STRING);
}
}
@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 = this.mark.mark(request);
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()) 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 RequestThreadTimes clone() {
RequestThreadTimes threadTimes = new RequestThreadTimes();
threadTimes.times = this.times;
threadTimes.request = FunRequest.cloneRequest(request);
threadTimes.mark = deepClone(mark);
return threadTimes;
}
}
我自己写了一个使用Demo:
def "测试并发情况下记录响应标记符的"() {
given:
HttpGet httpGet = FanLibrary.getHttpGet("https://cn.bing.com/");
MarkRequest mark = new MarkRequest() {
String m;
@Override
public String mark(HttpRequestBase base) {
base.removeHeaders("requestid");
m = m == null ? RString.getStringWithoutNum(4) : m
String value = "fun_" + m + CONNECTOR + Time.getTimeStamp();
base.addHeader("requestid", value);
return value;
}
};
FanLibrary.getHttpResponse(httpGet);
HttpClientConstant.MAX_ACCEPT_TIME = -1
RequestThreadTimes threadTimes = new RequestThreadTimes(httpGet, 2, mark);
new Concurrent(threadTimes, 2).start();
output(RequestThreadTimes.requestMark)
}
下面是记录的结果如下,可以看到,一共出现了两个 m 的值,后面跟的是时间戳,这样既保证了requestID唯一性,也可以对线程进行归类。
80_fun_QkhQ_1578367527661
103_fun_QkhQ_1578367527742
101_fun_zwtk_1578367527661
107_fun_zwtk_1578367527763
点赞关注!加入我们,642830685。群内免费领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!技术大牛解惑答疑,同行一起交流。