RocketJ 2020-01-11
在做性能测试过程中,遇到一个棘手的问题,开发让我们复现几个请求时间较长的请求,他们看日志进行链路追踪,查找瓶颈所在。
这里说一下框架中的处理逻辑:每个请求有一个唯一的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
实现类中使用这个接口对象,两个实现类的代码已经发过了性能测试框架第二版,这里只写一个:
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