B站面试官:你对性能测试框架理解多少?

之前写过一个性能测试框架,只是针对单一的HTTP接口的测试,对于业务接口和非HTTP接口还无非适配,刚好前端时间工作中用到了,就更新了自己的测试框架,这次不再以请求为基础,而是以方法为基础,这样就可以避免了单一性,有一个base类,然后其他的各种单一性请求在单独写一个适配类就好了,如果只是临时用,直接重新实现base即可。下面分享:

package com.fun.frame.thead;
import com.fun.frame.SourceCode;import com.fun.frame.excute.Concurrent;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import java.util.ArrayList;import java.util.List;import java.util.concurrent.CountDownLatch;
import static com.fun.utils.Time.getTimeStamp;
/** * 多线程任务基类,可单独使用 */public abstract class ThreadBase<T> extends SourceCode implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(ThreadBase.class);
    /**     * 任务请求执行次数     */    public int times;
    /**     * 计数锁     * <p>     * 会在concurrent类里面根据线程数自动设定     * </p>     */    CountDownLatch countDownLatch;
    /**     * 用于设置访问资源     */    public T t;
    public ThreadBase(T t) {        this();        this.t = t;    }
    public ThreadBase() {        super();    }
    /**     * groovy无法直接访问t,所以写了这个方法     *     * @return     */    public String getT() {        return t.toString();    }
    @Override    public void run() {        try {            before();            List<Long> t = new ArrayList<>();            long ss = getTimeStamp();            for (int i = 0; i < times; i++) {                long s = getTimeStamp();                doing();                long e = getTimeStamp();                t.add(e - s);            }            long ee = getTimeStamp();            logger.info("执行次数:{},总耗时:{}", times, ee - ss);            Concurrent.allTimes.addAll(t);        } catch (Exception e) {            logger.warn("执行任务失败!", e);        } finally {            after();            if (countDownLatch != null)                countDownLatch.countDown();        }    }
    /**     * 运行待测方法的之前的准备     */    protected abstract void before();
    /**     * 待测方法     *     * @throws Exception     */    protected abstract void doing() throws Exception;
    /**     * 运行待测方法后的处理     */    protected abstract void after();
    public void setCountDownLatch(CountDownLatch countDownLatch) {        this.countDownLatch = countDownLatch;    }
    public void setTimes(int times) {        this.times = times;    }
}

下面是几个实现过的基础类:

package com.fun.frame.thead;
import com.fun.httpclient.ClientManage;import com.fun.httpclient.FanLibrary;import com.fun.httpclient.GCThread;import org.apache.http.HttpStatus;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpRequestBase;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import java.io.IOException;
/** * http请求多线程类 */public class RequestThread extends ThreadBase {
    static Logger logger = LoggerFactory.getLogger(RequestThread.class);
    /**     * 请求     */    public HttpRequestBase request;
    /**     * 单请求多线程多次任务构造方法     *     * @param request 被执行的请求     * @param times   每个线程运行的次数     */    public RequestThread(HttpRequestBase request, int times) {        this.request = request;        this.times = times;    }
    @Override    public void before() {        request.setConfig(FanLibrary.requestConfig);        GCThread.starts();    }
    @Override    protected void doing() throws Exception {        getResponse(request);    }
    @Override    protected void after() {        GCThread.stop();    }
    /**     * 多次执行某个请求,但是不记录日志,记录方法用 loglong     * <p>此方法只适应与单个请求的重复请求,对于有业务联系的请求暂时不能适配</p>     *     * @param request 请求     * @throws IOException     */    void getResponse(HttpRequestBase request) throws IOException {        CloseableHttpResponse response = ClientManage.httpsClient.execute(request);        String content = FanLibrary.getContent(response);        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)            logger.warn("响应状态码:{},响应内容:{}", content, response.getStatusLine());        if (response != null) response.close();    }}

下面是数据库的:

package com.fun.frame.thead;
import com.fun.interfaces.IMySqlBasic;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import java.sql.SQLException;
/** * 数据库多线程类 */public class QuerySqlThread extends ThreadBase {
    private static Logger logger = LoggerFactory.getLogger(QuerySqlThread.class);
    String sql;
    IMySqlBasic base;
    public QuerySqlThread(IMySqlBasic base, String sql, int times) {        this.times = times;        this.sql = sql;        this.base = base;    }
    @Override    public void before() {        base.getConnection();    }
    @Override    protected void doing() throws SQLException {        base.excuteQuerySql(sql);    }
    @Override    protected void after() {        base.mySqlOver();    }}

下面是concurrent类:​​​​​​​

package com.fun.frame.excute;
import com.fun.bean.PerformanceResultBean;import com.fun.frame.Save;import com.fun.frame.SourceCode;import com.fun.frame.thead.ThreadBase;import com.fun.profile.Constant;import com.fun.utils.Time;import com.fun.utils.WriteRead;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import java.util.List;import java.util.Vector;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class Concurrent {
    private static Logger logger = LoggerFactory.getLogger(Concurrent.class);
    /**     * 线程任务     */    public ThreadBase thread;
    public List<ThreadBase> threads;
    public int num;
    public static Vector<Long> allTimes = new Vector<>();
    ExecutorService executorService;
    CountDownLatch countDownLatch;
    /**     * @param thread 线程任务     * @param num    线程数     */    public Concurrent(ThreadBase thread, int num) {        this(num);        this.thread = thread;    }
    /**     * @param threads 线程组     */    public Concurrent(List<ThreadBase> threads) {        this(threads.size());        this.threads = threads;    }
    public Concurrent(int num) {        this.num = num;        executorService = Executors.newFixedThreadPool(num);        countDownLatch = new CountDownLatch(num);    }    /**     * 执行多线程任务     */    public PerformanceResultBean start() {        long start = Time.getTimeStamp();        for (int i = 0; i < num; i++) {            ThreadBase thread = getThread(i);            thread.setCountDownLatch(countDownLatch);            executorService.execute(thread);        }        shutdownService(executorService, countDownLatch);        long end = Time.getTimeStamp();        logger.info("总计" + num + "个线程,共用时:" + Time.getTimeDiffer(start, end) + "秒!");        return over();    }
    private static void shutdownService(ExecutorService executorService, CountDownLatch countDownLatch) {        try {            countDownLatch.await();            executorService.shutdown();        } catch (InterruptedException e) {            logger.warn("线程池关闭失败!", e);        }    }
    private PerformanceResultBean over() {        Save.saveLongList(allTimes, num);        return countQPS(num);    }
    ThreadBase getThread(int i) {        if (threads == null) return thread;        return threads.get(i);    }
    /**     * 计算结果     * <p>此结果仅供参考</p>     *     * @param name 线程数     */    public static PerformanceResultBean countQPS(int name) {        List<String> strings = WriteRead.readTxtFileByLine(Constant.LONG_Path + name + Constant.FILE_TYPE_LOG);        int size = strings.size();        int sum = 0;        for (int i = 0; i < size; i++) {            int time = SourceCode.changeStringToInt(strings.get(i));            sum += time;        }        double v = 1000.0 * size * name / sum;        PerformanceResultBean performanceResultBean = new PerformanceResultBean(name, size, sum / size, v);        performanceResultBean.print();        return performanceResultBean;    }}

redis实现类缺失,因为没有遇到需要单独实现的需求。

 

关于用代码还是用工具实现并发,我个人看法所有所长,单究其根本,必然是代码胜于工具,原因如下:门槛高,适应性强;贴近开发,利于调优。性能测试,并发只是开始,只有一个好的开始才能进行性能数据分析,性能参数调优。所以不必拘泥于到底使用哪个工具那种语言,据我经验来说:基本的测试需求都是能满足的,只是实现的代价不同。

 

groovy是一种基于JVM的动态语言,我觉得最大的优势有两点,第一:于java兼容性非常好,大部分时候吧groovy的文件后缀改成java直接可以用,反之亦然。java的绝大部分库,groovy都是可以直接拿来就用的。这还带来了另外一个有点,学习成本低,非常低,直接上手没问题,可以慢慢学习groovy不同于Java的语法;第二:编译器支持变得更好,现在用的intellij的ide,总体来说已经比较好的支持groovy语言了,写起代码来也是比较顺滑了,各种基于groovy的框架工具也比较溜,特别是Gradle构建工具,比Maven爽很多。----此段文字为了撑字数强加的,与内容无关。

 

最后

俺叫小枫,一个成天想着一夜暴富的测试员

(1140267353)一起成长一起加油的伙伴群!软件测试,与你同行!
群内可领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!

点赞关注不迷路!!!【三连ღ】,有问题也可私聊哟~(*╹▽╹*)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值