「All right reserved, any unauthorized reproduction or transfer is prohibitted」
        
      之前写过一个性能测试框架中 QPS 取样器实现,总体思路是通过一个异步工具类com.funtester.frame.execute.Progress来统计各个线程自己的统计数据(响应时间),然后再依据线程数计算实时的 QPS。
但是这个思路很容易受到数据(响应时间)取样的样本大小影响,因为对于混合接口压测场景中,不同接口的响应时间可能会差别很大。所有通过之前的思路就需要优化了,经过一些尝试终于确定了新的方案。
思路
这个功能仅针对于静态压测模型来讲,因为动态压测模型实现思路已经很不一样了,强制统一不太合适。所以针对动态模型,我单独写了一个动态模型的 QPS 取样器,对于之前的静态模型,我重新实现了 RT 取样器。由于动态模型中并不会记录响应时间,暂时也没有增加 RT 取样器的计划。
- 对于静态模型的 RT 取样器。我添加了
com.funtester.base.constaint.ThreadBase#INTERCEPT和com.funtester.base.constaint.ThreadBase#COUNT,通过控制这两个开关来实现不同的功能。通过一个异步线程接口控制台输出内容,来控制 RT 取样器工作状态。 - 对于动态模型中的 QPS 模型,用到了性能测试中的 LongAdder,经过测试性能足够好。依然通过异步线程获取这个计数器的值当做 QPS,然后每次重置即可。
 - 对于动态模型中的线程模型,由于
com.funtester.base.constaint.ThreadBase已经提供了com.funtester.base.constaint.ThreadBase#executeNum当做每个线程的计数器,由于单线程的,所以并不用考虑线程安全的问题。直接将任务池中的com.funtester.base.constaint.ThreadBase存活对象遍历一遍求和即可。 
实现
静态模型
取样器方法实现:
/**
 * 取样器
 *
 * @param time
 */
public static void statistic(int time) {
    INTERCEPT = true;
    sleep(time);
    INTERCEPT = false;
    CountUtil.FunIndex index = CountUtil.index(interceptCosts);
    logger.info("当前QPS:{}", interceptCosts.size() / time);
    logger.info("当前RT:{}", index.toString());
    interceptCosts = new Vector<>();
}
 
这里用到了com.funtester.utils.CountUtil#index,代码后面会附上。
动态 QPS 模型
之前写过一个基于Disruptor的动态 QPS 模型框架:
- 
高性能队列 Disruptor 在测试中应用  
2021-12-28 - 
千万级日志回放引擎设计稿  
2021-12-30 
但是经过实际使用,并不适合,二次开发的复杂程度较高,目前已经放弃了。不过由于部分用例已经用上了,新的框架还没时间验证,所以还会保留一阵子。
关于Disruptor获取实时 QPS 的可以翻一翻上面的文章,下面是新写的框架实现:
if (index++ % LOOP_INTERVAL == 0) logger.info("当前设计QPS:{},实际QPS:{} 活跃线程数:{}", qps, total.sumThenReset() / LOOP_INTERVAL as int, executor.getActiveCount())
 
其中com.funtester.frame.execute.FunEventConcurrent#total作为单个测试任务的统计对象,这也是为以后多任务做铺垫。
动态线程模型
这个相对简单,不用考虑线程安全的问题,只是在每次输出增减内容之后输出当前任务池的信息即可。
首先是任务池信息获取方法:
/**
 * 获取实时当然任务池信息
 */
public static synchronized void printInfo() {
    long s = threads.stream().collect(Collectors.summarizingInt(f -> f.executeNum)).getSum();
    sleep(1);
    long e = threads.stream().collect(Collectors.summarizingInt(f -> f.executeNum)).getSum();
    logger.info("当前任务数:{} QPS:{}", aliveSize(), e - s);
}
 
其次是在处理控制台输出内容的内部类稍微改造一下:
private static class FunTester implements IFunController {
    boolean key = true;
    @Override
    public void run() {
        while (key) {
            String input = getInput();
            switch (input) {
                case "+":
                    add();
                    break;
                case "-":
                    reduce();
                    break;
                case "*":
                    over();
                    key = false;
                    break;
                default:
                    if (Regex.isMatch(input, "(F|f)\\d+")) THREAD_STEP = changeStringToInt(input.substring(1));
                    break;
            }
            FunThread.printInfo();
        }
    }
    @Override
    public void add() {
        range(THREAD_STEP).forEach(f -> {
            addTask();
            sleep(0.5);
        });
    }
    @Override
    public void reduce() {
        range(THREAD_STEP).forEach(f -> {
            removeTask();
            sleep(0.5);
        });
    }
    @Override
    public void over() {
        FunThread.stop();
        logger.info("动态结束任务!");
    }
}
 Have Fun ~ Tester !
- FunTester 原创大赏
 - 性能测试专题【FunTester 原创】
 - FunTester 社群风采
 - 接口功能测试专题【FunTester 原创】
 - Java、Groovy、Python 和 Golang 如何把方法当作参数
 - Socket 接口固定 QPS 性能测试实践
 - JSON 必知必会【PDF+ 视频教程】
 - 利用 Python+plotly 制作接口请求时间的 violin 图表
 - 初遇 Postman,SayHi 的三种方式
 - 6 个重要的 JVM 性能参数
 - Java&Go 三种 HTTP 客户端性能测试
 - JMeter 吞吐量误差分析
 - Selenium4 Alpha-7 升级体验
 - 基于爬虫的测试自动化经验分享
 - 利用闭包实现自定义等待方法
 
          TesterHome 为用户提供「保留所有权利,禁止转载」的选项。
          除非获得原作者的单独授权,任何第三方不得转载标注了「All right reserved, any unauthorized reproduction or transfer is prohibitted」的内容,否则均视为侵权。
          具体请参见TesterHome 知识产权保护协议。
  
    如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
  
  
      No Reply at the moment.