• 单链路性能测试实践


    在经历过一些尝试之后,觉得在当下的项目中运用链路压测的能力,不等着其他人了。

    关于链路

    链路这个词其实不如路径通俗易懂,跟产品沟通这个比较有效率。具体的操作路径,产品会给一份出来,但是这都是基于UI和产品思维的文档,跟接口测试区别还是很大的,只能提供参考依据。

    需要端上测试协作,有些业务细节还得端上测试同学帮忙补充一下。还需要运维同事帮忙理一下各个接口的请求量比例,这次的比例我是依据灵光一现写出来,然后大家一起调整的。

    本次由于比较初级,所以这块文档就不写出来了,放一个图来表达一下这个链路做了些什么,PS:我现在很喜欢用图而不是文字,沟通效率太高了。推荐工具draw.io,感兴趣的可以参考文末的热文中两张架构图中的介绍。

    资源库1.4链路压测方案

    • 这次把登录剔除了,因为太慢了,对测试结果影响比较大。

    场景思路

    场景

    场景就是老师登录,首先会请求一个知识点列表,然后通过知识点属性筛选推荐课程列表,在对课程列表中的数据进行收藏和取消收藏,在获取自己当前知识点下的课程列表(包含原创和收藏)。

    思路

    本次依然采取固定线程的压测模型,本人预估线程200左右,测试用户600备用,列表页保证2页数据。

    每个线程绑定一个用户,然后用户开始循环链路执行步骤,执行一次当做一次Q。单次Q包含9HTTP接口请求(放弃了Socket接口,以后有需求再添加Socket接口到链路中),其中3次修改操作,6次查询操作。

    具体的逻辑通过内部静态类实现,然后多一个K类,用来存储每次获取的知识点属性,方便调用。由于接口请求方法都是用基础数据类型和String作为参数,所以调用时候会显得有点啰嗦。但无伤大雅,脚本写出来,本来就是用完就扔到仓库里面,改天再用再优化。

    Demo实现

    package com.okayqa.composer.performance.resource1_4
    
    import com.alibaba.fastjson.JSON
    import com.alibaba.fastjson.JSONObject
    import com.funtester.base.bean.AbstractBean
    import com.funtester.base.constaint.ThreadLimitTimesCount
    import com.funtester.frame.execute.Concurrent
    import com.funtester.httpclient.ClientManage
    import com.funtester.utils.ArgsUtil
    import com.okayqa.composer.base.OkayBase
    import com.okayqa.composer.function.Mirro
    import com.okayqa.composer.function.OKClass
    
    class Login_collect_uncollect extends OkayBase {
    
        public static void main(String[] args) {
            ClientManage.init(10, 5, 0, "", 0)
            def util = new ArgsUtil(args)
            def thread = util.getIntOrdefault(0, 30)
            def times = util.getIntOrdefault(1, 40)
    
            def tasks = []
    
            thread.times {
                tasks << new FunTester(it, times)
            }
    
            new Concurrent(tasks, "资源库1.4登录>查询>收藏>取消收藏链路压测").start()
    
            allOver()
        }
    
        private static class FunTester extends ThreadLimitTimesCount<Integer> {
    
            OkayBase base
    
            def mirro
    
            def clazz
    
            FunTester(Integer integer, int times) {
                super(integer, times, null)
            }
    
            @Override
            void before() {
                super.before()
                base = getBase(t)
                mirro = new Mirro(base)
                clazz = new OKClass(base)
            }
    
            @Override
            protected void doing() throws Exception {
    
                def klist = mirro.getKList()
                def karray = klist.getJSONArray("data")
                K ks
                karray.each {
                    JSONObject parse = JSON.parse(JSON.toJSONString(it))
                    if (ks == null) {
                        def level = parse.getIntValue("node_level")
                        def type = parse.getIntValue("ktype")
                        def id = parse.getIntValue("id")
                        ks = new K(id, type, level)
                    }
                }
                JSONObject response = clazz.recommend(ks.id, ks.type, ks.level)
                def minis = []
                int i = 0
                response.getJSONArray("data").each {
                    if (i++ < 2) {
                        JSONObject parse = JSON.parse(JSON.toJSONString(it))
                        int value = parse.getIntValue("minicourse_id")
                        minis << value
                    }
                }
                clazz.unCollect(random(minis))
    
                mirro.getMiniCourseListV3(ks.id, ks.type, 0, ks.level)
            }
        }
    
        private static class K extends AbstractBean {
    
            int id
    
            int type
    
            int level
    
            K(int id, int type, int level) {
                this.id = id
                this.type = type
                this.level = level
            }
        }
    
    }
    
    
    

    其中AbstractBean类是一个抽象类,用于一些bean的方法封装,就是为了省事儿。

    package com.funtester.base.bean
    
    import com.alibaba.fastjson.JSON
    import com.alibaba.fastjson.JSONObject
    import com.funtester.frame.Save
    import com.funtester.frame.SourceCode
    import org.slf4j.Logger
    import org.slf4j.LoggerFactory
    import org.springframework.beans.BeanUtils
    
    /**
     * bean的基类
     */
    abstract class AbstractBean {
    
        static final Logger logger = LoggerFactory.getLogger(AbstractBean.class)
    
        /**
         * 将bean转化为json,为了进行数据处理和打印
         *
         * @return
         */
        JSONObject toJson() {
            JSONObject.parseObject(JSONObject.toJSONString(this))
        }
    
        /**
         * 文本形式保存
         */
        def save() {
            Save.saveJson(this.toJson(), this.getClass().toString() + SourceCode.getMark());
        }
    
        /**
         * 控制台打印,使用WARN记录,以便查看
         */
        def print() {
            logger.warn(this.getClass().toString() + ":" + this.toString());
        }
    
        def initFrom(String str) {
            JSONObject.parseObject(str, this.getClass())
        }
    
        def initFrom(Object str) {
            initFrom(JSON.toJSONString(str))
        }
    
        def copyFrom(AbstractBean source) {
            BeanUtils.copyProperties(source, this)
        }
    
        def copyTo(AbstractBean target) {
            BeanUtils.copyProperties(this, target)
        }
    
        /**
         * 这里bean的属性必需是可以访问的,不然会返回空json串
         * @return
         */
        @Override
        String toString() {
            JSONObject.toJSONString(this)
        }
    
        @Override
        protected Object clone() {
            initFrom(this)
        }
    
    }
    
    

    控制台输出

    ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
    >  {
    >  ① . "rt":1665,
    >  ① . "total":1188,
    >  ① . "qps":18.018,
    >  ① . "failRate":0.0,
    >  ① . "threads":30,
    >  ① . "startTime":"2021-02-24 16:57:23",
    >  ① . "endTime":"2021-02-24 16:58:34",
    >  ① . "errorRate":1.01,
    >  ① . "executeTotal":1188,
    >  ① . "mark":"资源库1.4登录>查询>收藏>取消收藏链路压测241657",
    >  ① . "table":"eJzj5VLAD15sbXm2a8LTXZMN9Uyez9z9dO9Uu2fzl75Yv8ju2ZRtL6b32z3tn/ZsWweE83Lyvhfb1z/t6362tZvT2EChJKMoNaWYgA0KvFy8+F0RlFpckJ9XnKoQkpmbaqVQoVucWpSZmKOQV5qro1Cpm5uakpmYR8gOQq5QyM3MU4AYZWVhYqmQW6yTm1hhZWxoaQxkE9RNjA2UgEfTOoBo1JZRW2hmRSsQ0ccmsBW0tgnVQzS1DatVtLMRn3W0sPXRtCYgAlLtQITXWura/mhaMxCRYC+VXUGyv2nhmkfTGoGI0rCgrsseTWsBImLSIZ1dCA8sOjmMXNfCU9ZAupNYV8MdC4106qdA2vkAniAGq6OJ8AlVi6GB99FgSvTU8BU8egaBg6jsO3iWHwSuoZUPB4EzRn046sNRHw68M0Z9OOrDEe5DABkr1eo="
    >  }
    ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
    

    资源库1.4登录>查询>收藏>取消收藏链路压测


    FunTester腾讯云社区钦定年度作者,非著名测试开发er,欢迎关注。

  • 相关阅读:
    【Azure Developer】使用Azure PubSub服务示例代码时候遇见了.NET 6.0的代码转换问题
    【Azure 事件中心】适用Mirror Maker生产数据发送到Azure Event Hub出现发送一段时间后Timeout Exception: Expiring 18 record(s) for xxxxxxx: 79823 ms has passed since last append
    【Azure API 管理】 为APIM创建一个审批订阅申请的RBAC角色,最少的Action内容是什么呢?
    【Azure 应用服务】Azure App Service多实例中,出现某一个实例CPU居高不下的情况,如何重启单个实例呢?
    【Azure 存储服务】Java Azure Storage SDK V12使用Endpoint连接Blob Service遇见 The Azure Storage endpoint url is malformed
    【Azure 应用服务】App Service 在修改应用服务计划的页面中,为什么无法查看到同一个资源组下面的其他应用服务计划(App Service Plan)呢?
    【Azure 应用服务】App Service for Windows 环境中为Tomcat自定义4xx/5xx页面
    SpringBoot自动注入后,找不到BCryptPasswordEncoder类型的Bean
    docker容器部分命令
    国内服务器访问github解决方法
  • 原文地址:https://www.cnblogs.com/FunTester/p/14446088.html
Copyright © 2020-2023  润新知