专栏首页FunTester性能测试中标记每个请求

性能测试中标记每个请求

在做性能测试过程中,遇到一个棘手的问题,开发让我们复现几个请求时间较长的请求,他们看日志进行链路追踪,查找瓶颈所在。

这里说一下框架中的处理逻辑:每个请求有一个唯一的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);


}

然后我再ThreadLimitTimesCountThreadLimitTimeCount实现类中使用这个接口对象,两个实现类的代码已经发过了性能测试框架第二版,这里只写一个:

  • 中间用到了深拷贝的方法,在之前也记录过了拷贝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

  • 郑重声明:文章首发于公众号“FunTester”,禁止第三方(腾讯云除外)转载、发表。

本文分享自微信公众号 - FunTester(NuclearTester),作者:八音弦

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 性能测试框架第三版

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

    FunTester
  • arthas命令watch观察方法调用(下)

    上期讲过了arthas命令watch观察方法调用(上)中官方文档和官方的Demo演示,本期讲一下watch命令的实践,主要内容是针对Demo里面用到的命令进行演...

    FunTester
  • 我的开发日记(五)

    今天主要学习使用了比较常用的功能:1:更新数据时从其他表读取数据(这里有个问题,有个更好的办法是inner join,但是我这个更新涉及到了修改关联项apiId...

    FunTester
  • 统一管理项目中的接口回调

    无论是 Java Web 开发还是 Android 开发我们都避免不了异步处理业务的逻辑。有异步的出现一定就有异步结果(接口)的回调。你之前是不是有为每一个异步...

    Javen
  • ssh登录实现

    工程目录 ? 配置文件详解 Spring的applicationContext.xml文件 <span ><?xml version="1.0" encodin...

    用户1141560
  • 【系统设计】基于角色的权限管理设计实现

    内部运营系统的很多 API,涉及到外网正式环境下的用户信息变更。出于安全考虑,在设计之初保留了所有的操作记录,但这用于事后回查;真正要避免线上事故的发生,还需要...

    心谭博客
  • 动态代理之投鞭断流!看一下MyBatis的底层实现原理!

    一日小区漫步,我问朋友:Mybatis中声明一个interface接口,没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库数据,你知道...

    用户5224393
  • MyBatis的底层实现原理!是动态代理的运用~

    一日小区漫步,我问朋友:Mybatis中声明一个interface接口,没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库数据,你知道...

    java思维导图
  • 动态代理之投鞭断流!看一下MyBatis的底层实现原理!

    一日小区漫步,我问朋友:Mybatis中声明一个interface接口,没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库数据,你知道...

    Java后端技术
  • ES 7.8 速成笔记(中)

    习惯于数据库开发的同学,自然最喜欢这种方式。为了方便讲解,先写一段代码,生成一堆记录

    菩提树下的杨过

扫码关注云+社区

领取腾讯云代金券