zoukankan      html  css  js  c++  java
  • 线程同步类CyclicBarrier在性能测试集合点应用

    在之前的性能测试方案设计中,如果是涉及到多用户的,我一般都是通过先登录用户,然后再将Base对象传入多线程任务类,以此进行性能测试。

    但是这种处理方式有个问题,就是在执行多线程任务类之前,可能会造成等待时间过多,因为需要串行登录用户,如果线程过多的话,等待的时间会稍等长一点。

    为此我找到了一个解决办法,就是使用线程同步类CyclicBarrier将用户登录过程在多线程中实现,然后所有用户登录完成之后再进行性能测试方法的执行,简单讲就是设置一个多线程集合点,所有线程都到达集合点之后,再一起执行具体的测试方法。

    之前的文章又介绍过多线程同步类CountDownLatchCyclicBarrierPhaser,以及在我之前的性能测试过程中的应用,文章列表如下:

    需求

    分两步:第一步记录一条数据(有唯一性验证);第二步更改该条记录的状态。

    描述比较模糊,简单理解就是insert之后update,业务功能简单。

    用例设计思路

    虽然业务简单,但是实现比较麻烦,所以我采取了链路性能测试,确定唯一性标记,然后进行update,避免了,单独测试造数据太麻烦的问题。

    伪代码如下:

    @Override
            protected void doing() {
                String orderNum = "FunTester" + getMark() + StringUtil.getString(10)
                threadmark += orderNum
                order.insert(orderNum)
                order.update(orderNum)
            }
    

    这里将orderNum当做标记对象了,用于链路追踪和日志查询。

    多线程类实现

    我继续采取ThreadLimitTimesCount<Integer>类作为模型类的内部静态类实现,定长线程和固定次数。

    
    private static class FunTester extends ThreadLimitTimesCount<Integer> {
    
            Order order
    
            CyclicBarrier cyclicBarrier
    
            FunTester(int u, int times, CyclicBarrier cyclicBarrier) {
                super(u, times, null)
                this.cyclicBarrier = cyclicBarrier
            }
    
            @Override
            void before() {
                super.before()
                this.order = new Order(getBase(t))
                cyclicBarrier.await()
            }
    
            @Override
            protected void doing() {
                String orderNum = "f" + getMark() + StringUtil.getString(5)
                threadmark += orderNum
                order.create(101, "FunTester测试课程", 13120454219, 10, 10, orderNum)
                order.refund(orderNum)
            }
    
        }
    

    这里用到了cyclicBarrier.await()方法,使得所有线程达到该集合点之后,才进行下一步的代码执行。

    测试脚本

    static void main(String[] args) {
    
            Common.notPrintResponse()
            ClientManage.init(10, 5, 0, EMPTY, 0)
            def argsUtil = new ArgsUtil(args)
            def thread = argsUtil.getIntOrdefault(0, 10)
            def times = argsUtil.getIntOrdefault(1, 10)
            def threads = []
            CyclicBarrier cyclicBarrier = new CyclicBarrier(thread, new Runnable() {
    
                @Override
                void run() {
                    logger.info("所有账号登录完成!,即将开始测试!")
                }
            })
            thread.times {
                threads << new FunTester(it, times, cyclicBarrier)
            }
    
            new Concurrent(threads, "一个不可描述的用例场景").start()
    
    
            FunLibrary.testOver()
    
        }
    

    Common.notPrintResponse()方法为了屏蔽测试过程中,打印响应结果,因为日志太多了,处理起来比较麻烦。最近在研究链路测试中对各个接口的数据处理,所以想到了这个方法。

    控制台输出

    INFO-> 当前用户:fv,IP:10.60.193.37,工作目录:/Users/fv/Documents/workspace/qa/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
    INFO-> 本校共有:627名老师,852名学生,11个班级!
    INFO-> 请求uri:不可描述的地址/login,耗时:458 ms, requestId:Fun20210310111251QvEl
    INFO-> 请求uri:不可描述的地址/login,耗时:458 ms, requestId:Fun20210310111251xQKM
    INFO-> 用户:82951571527,登录成功!
    INFO-> 用户:82951571522,登录成功!
    INFO-> 请求uri:不可描述的地址/login,耗时:458 ms, requestId:Fun20210310111251TLml
    INFO-> 用户:82951571529,登录成功!
    INFO-> 请求uri:不可描述的地址/login,耗时:471 ms, requestId:Fun20210310111251CgTd
    INFO-> 用户:82951571531,登录成功!
    INFO-> 请求uri:不可描述的地址/login,耗时:483 ms, requestId:Fun20210310111251YQyQ
    INFO-> 请求uri:不可描述的地址/login,耗时:459 ms, requestId:Fun20210310111251IGUs
    INFO-> 请求uri:不可描述的地址/login,耗时:458 ms, requestId:Fun20210310111251NZQt
    INFO-> 请求uri:不可描述的地址/login,耗时:484 ms, requestId:Fun20210310111251KVRP
    INFO-> 用户:82951571525,登录成功!
    INFO-> 用户:82951571513,登录成功!
    INFO-> 用户:82951571514,登录成功!
    INFO-> 用户:82951571516,登录成功!
    INFO-> 请求uri:不可描述的地址/login,耗时:604 ms, requestId:Fun20210310111251AXdV
    INFO-> 用户:82951571518,登录成功!
    INFO-> 请求uri:不可描述的地址/login,耗时:610 ms, requestId:Fun20210310111251KUMm
    INFO-> 用户:82951571524,登录成功!
    INFO-> 所有账号登录完成!,即将开始测试!
    
    *********中间省略N次请求日志*************
    
    INFO-> 线程:一个不可描述的用例场景2,执行次数:10,错误次数: 0,总耗时:2.317 s
    INFO-> 一个不可描述的用例场景进度:▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍  100%
    INFO-> 总计10个线程,共用时:3.132 s,执行总数:100,错误数:0,失败数:0
    INFO-> 数据保存成功!文件名:/Users/fv/Documents/workspace/qa/long/data/易视腾3.3购买退款101112_10
    INFO-> 
    ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
    >  {
    >  ① . "rt":210,
    >  ① . "total":100,
    >  ① . "qps":47.619,
    >  ① . "failRate":0.0,
    >  ① . "threads":10,
    >  ① . "startTime":"2021-03-10 11:12:51",
    >  ① . "endTime":"2021-03-10 11:12:54",
    >  ① . "errorRate":0.0,
    >  ① . "executeTotal":100,
    >  ① . "mark":"一个不可描述的用例场景101112",
    >  ① . "table":"eJwBHQDi/+aVsOaNrumHj+WkquWwkSzml6Dms5Xnu5jlm74hMCkTtQ=="
    >  }
    ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
    INFO-> 数据量太少,无法绘图!
    

    最后因为测试请求,数据量太少了,所以屏蔽了画图功能,欲知图像如何,请参考:性能测试中图形化输出测试数据


    FunTester腾讯云年度作者、Boss直聘签约作者,非著名测试开发er,欢迎关注。

  • 相关阅读:
    haproxy 2.5 发布
    cube.js sql 支持简单说明
    基于graalvm 开发一个cube.js jdbc driver 的思路
    apache kyuubi Frontend 支持mysql 协议
    oceanbase 资源池删除说明
    基于obd 的oceanbase 扩容说明
    jfilter一个方便的spring rest 响应过滤扩展
    cube.js schema 定义多datasource 说明
    typescript 编写自定义定义文件
    meow 辅助开发cli 应用的工具
  • 原文地址:https://www.cnblogs.com/FunTester/p/14518520.html
Copyright © 2011-2022 走看看