在之前的性能测试方案设计中,如果是涉及到多用户的,我一般都是通过先登录用户,然后再将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,欢迎关注。


↙↙↙阅读原文可查看相关链接,并与作者交流