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,欢迎关注。

  • 相关阅读:
    【译】StackExchange.Redis 中文文档(十)性能分析
    【译】StackExchange.Redis 中文文档(九)服务器相关命令
    【译】StackExchange.Redis 中文文档(八)流
    【译】StackExchange.Redis 中文文档(七)推送/订阅消息顺序
    【译】StackExchange.Redis 中文文档(六)事件
    【译】StackExchange.Redis 中文文档(五)事务
    查看供应商2086报表
    创建内部供应商
    创建客户前台配置
    创建客户后台配置-spro
  • 原文地址:https://www.cnblogs.com/FunTester/p/14518520.html
Copyright © 2011-2022 走看看