专栏首页FunTester性能测试框架第三版

性能测试框架第三版

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

这两天又做了一些优化。主要方向还是将标记mark功能放到threadbase中,将mark方法的参数类型由httprequestbase变成threadbase,把原来有request并发实现类中实现的带有标记的run()方法,改到有两种模式虚拟类ThreadLimitTimesCountThreadLimitTimeCount中实现,放弃了实现类中再重写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<T>代码:

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;
    }


}


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

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

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

原始发表时间:2020-02-03

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 性能测试中标记每个请求

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

    FunTester
  • 定时和定量压测模式实现--视频讲解

    上期将了多线程基类和执行类,本期分享一下两种压测模式。一种是定量,即单线程循环次数固定;另一种是定时,即单线程执行时间固定。两种方式各有优劣,实际工作中个人偏向...

    FunTester
  • 性能测试框架

    之前写过一个性能测试框架,只是针对单一的HTTP接口的测试,对于业务接口和非HTTP接口还无非适配,刚好前段时间工作中用到了,就更新了自己的测试框架,这次不再以...

    FunTester
  • Hadoop(十一)Hadoop IO之序列化与比较功能实现详解

    前言   上一篇给大家介绍了Hadoop是怎么样保证数据的完整性的,并且使用Java程序来验证了会产生.crc的校验文件。这一篇给大家分享的是Hadoop的序列...

    用户1195962
  • 第80节:Java中的MVC设计模式

    事务,设置自动连接提交关闭. setAutoCommit(false); conn.commit(); conn.rollBack

    达达前端
  • Hadoop(十一)Hadoop IO之序列化与比较功能实现详解

      上一篇给大家介绍了Hadoop是怎么样保证数据的完整性的,并且使用Java程序来验证了会产生.crc的校验文件。这一篇给大家分享的是Hadoop的序列化!

    大道七哥
  • 详解Intellij IDEA搭建SpringBoot

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而...

    Java团长
  • 固定QPS压测模式探索

    在早前跟测试同行在QQ群聊天的时候,聊过一个固定QPS压测的问题,最近突然有需求,想实现一下,丰富一下自己的性能测试框架,最新的代码请移步我的GitHub,地址...

    FunTester
  • Kafka之拦截器Interceptor

        使用场景:我们可以在Producer端统一拦截,加上处理时间,再在consumer端统一拦截统计端到端的处理时间,这也是一种监控方式。

    克虏伯
  • Mybatis 源码分析(四)之 Mybatis 的执行流程梳理

    前面了解到Mybatis的执行流程,首先读取我们的mybatis-config.xml配置文件,然后构建Configuration类,这个类会像上下文信息一样会...

    zoro

扫码关注云+社区

领取腾讯云代金券