zoukankan      html  css  js  c++  java
  • DCS_FunTester分布式压测框架更新(一)

    在经过了一些人生的思考和积累,终于开始第一轮的更新,除了修复BUG以外,还进行不少功能性更新。另外本地部署已经完成了。有兴趣的可以自己多体验一下。分布式性能测试框架单节点内测

    确定名字

    确定项目名字DCS_FunTesterDCS全称Distributed Control System,既分布式控制系统。后缀FunTester。目前项目是slave项目,至于master项目还在思考。但是不影响slave项目的多节点部署。slave项目不依赖master项目,具有独立部署提供服务能力,当然也具有多节点部署能力。只是需要调用HTTP接口时候需要请求所有slave节点的接口。

    swagger支持

    尝试了一些方式,觉得swagger还是最优方案,接口文档地址:http://124.70.188.11:8080/swagger-ui/#,过段时间服务器到期,各位尝试本地部署时替换域名和端口即可。

    本地部署

    之前计划的本地部署,但是进度有点落后了,本周把代码推送到了gitee上,可以随意下载部署。具体的部署文档还没写,因为内测阶段有些东西还在调整。大体的部署步骤如下:

    构建FunTester

    gitee地址https://gitee.com/fanapi/tester,最新分支oker,直接拉取最近代码即可。
    构建工具Gradle ,执行build或者jar即可,不需要本地Groovy SDK

    构建DCS_FunTester

    此处依赖FunTesterjar包,gradle.build引用 compile files('/Users/oker/Library/groovy-3.0.8/lib/funtester-1.0.jar'),各位直接修改目录地址即可。
    gitee地址https://gitee.com/fanapi/dcs,默认分支master,拉取最近代码即可。
    构建工具Gradle ,执行build或者jar即可,不需要本地Groovy SDK

    启动服务

    目前还没使用docker,直接用的Java -jar **** &这样的命令行。

    更换验证方式

    由于之前的考虑不周,把密钥验证放在了接口参数中,导致后面的一系列问题。我已经换了一种方式:放在header中进行验证。忽略GET请求。这个功能仅限于公网内测服务的使用(由于云服务器到期,近期会下线),本地部署不需要。

    代码坐标org.funtester.dcs.common.wapper.WrappingFilter#doFilter,内容如下:

            String headerKey = req.getHeader(DcsConstant.HEADER_KEY);
            String method = requestWrapper.getMethod();
            if (!method.equalsIgnoreCase("get") && (StringUtils.isEmpty(headerKey) || !headerKey.equalsIgnoreCase(DcsConstant.HEADER_VALUE))) {
                response.getOutputStream().write(Result.fail("验证失败!").toString().getBytes());
                return;
            }
    

    本地部署可以删除此段。

    异步执行用例

    来处来

    之前测试接口是串行运行测试用例,在实际使用中会导致接口响应时间特别长,但是内测接口对于线程数和请求次数做了限制,所以基本也都控制在10s以内,这次的更新会放开限制,采用异步运行测试用例的方式。

    具体限制如下:

    class HttpRequest extends AbstractBean implements Serializable {
    
        private static final long serialVersionUID = 324324327948379L;
    
        @NotNull
        JSONObject request
    
        //压测模式
        @Range(min = 1L, max = 2000L)
        Integer times
    
        String mode;
        //线程数,这里默认固定线程模式
        @Range(min = 1L, max = 100L)
        Integer thread
    
        //软启动时间
        Integer runup
    
        //用例描述
        @Length(min = 1, max = 100)
        String desc
    
    }
    
    

    去处去

    异步执行思路比较简单,就是在处理中把com.funtester.frame.execute.Concurrent对象组装完成之后,调用一个异步执行的方法,返回一个标记值即可。使用如下:

        @PostMapping(value = "/post")
        public Result tests(@Valid @RequestBody HttpRequest request) {
            if (!ThreadBase.needAbort()) return Result.fail();
            JSONObject r = request.getRequest();
            HttpRequestBase re = FunRequest.initFromJson(r).getRequest();
            Integer times = request.getTimes();
            String mode = request.getMode();
            Integer thread = request.getThread();
            Integer runup = request.getRunup();
            String desc = request.getDesc();
            if (mode.equalsIgnoreCase("ftt")) {
                Constant.RUNUP_TIME = runup;
                RequestThreadTimes task = new RequestThreadTimes(re, times);
                Concurrent concurrent = new Concurrent(task, thread, desc);
                return Result.success(execute(concurrent));
            }
            return Result.fail();
        }
    

    其中execute方法内容如下:

        /**
         * 异步执行的用例的方法
         *
         * @param concurrent
         * @return
         */
        public int execute(Concurrent concurrent) {
            int mark = SourceCode.getMark();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    PerformanceResultBean start = concurrent.start();
                    FunData.results.put(mark, start);
                }
            }).start();
            return mark;
        }
    

    因为目前设计的一个slave节点同时只允许一个任务运行,所以这里暂不考虑线程安全的问题,认为mark唯一。

    org.funtester.dcs.common.FunData#results是一个ConcurrentHashMap<Integer, PerformanceResultBean>()用例保存测试用例执行结果已提供接口访问。由于资源有限(缺钱),没有进行数据库存储和用户区分。直接放在这个map里面了。用一个定时任务去定期清理里面的数据。

        /**
         * 定时删除存的过期数据
         * @return
         */
        @Scheduled(cron = "0 0 0/3 * * ?")
        def saveRequestBean() {
            def mark = SourceCode.getMark()
            List<Integer> dels = []
            FunData.results.keySet().each {
                if (mark - it > 7200) {
                    dels << it
                }
            }
            dels.each {FunData.results.remove(it)}
            logger.info("定时任务执行完毕! 时间:{}", Time.getDate())
        }
    
    
    

    同样的原因,这里也不考虑线程安全的问题。

    获取测试结果接口:

        @GetMapping(value = "/get/{id}")
        public Result getRunResult(@PathVariable(name = "id") int id) {
            PerformanceResultBean performanceResultBean = FunData.results.get(id);
            return Result.success(performanceResultBean);
        }
    

    多请求支持

    本次更新增加的多请求的线程压测支持。思路如下,在新建测试任务时候,将请求list存一下,然后再并发的时候依次去拿里面的请求发送出去,也可以采用随机取请求,或者用i++这种方式去取,都是可以的。这里不要求精度的话,三种方式都可以。至于流量编排(各个请求按比例发送)的功能,下期尽量添加上。

    实现代码如下:

        @PostMapping(value = "/post2")
        public Result tests2(@Valid @RequestBody HttpRequest2 request) {
            if (!ThreadBase.needAbort()) return Result.fail();
            JSONArray requests = request.getRequests();
            request.print();
            List<HttpRequestBase> res = new ArrayList<>();
            requests.forEach(f -> {
                res.add(FunRequest.initFromString(JSON.toJSONString(f)).getRequest());
            });
            Integer times = request.getTimes();
            String mode = request.getMode();
            Integer thread = request.getThread();
            Integer runup = request.getRunup();
            String desc = request.getDesc();
            if (mode.equalsIgnoreCase("ftt")) {
                Constant.RUNUP_TIME = runup;
                ListRequestMode task = new ListRequestMode(res, times);
                Concurrent concurrent = new Concurrent(task, thread, desc);
                return Result.success(execute(concurrent));
            }
            return Result.success();
        }
    

    自定义类org.funtester.dcs.template.ListRequestMode代码如下:

    package org.funtester.dcs.template
    
    import com.funtester.base.constaint.FixedThread
    import com.funtester.base.constaint.ThreadBase
    import com.funtester.httpclient.FunLibrary
    import org.apache.http.client.methods.HttpRequestBase
    
    class ListRequestMode<List> extends FixedThread {
    
    
        ListRequestMode(List<HttpRequestBase> res, int times) {
            super(res, times, true)
        }
    
        @Override
        protected void doing() throws Exception {
            //        FunLibrary.executeSimlple(res.get(index.getAndDecrement() % res.size()))
            FunLibrary.executeSimlple(random(f))
        }
    
        @Override
        ThreadBase clone() {
            return new ListRequestMode(f, limit);
        }
    }
    

    获取测试进度

    这里有个前提就是单个节点只能同时运行一个用例,所以我在com.funtester.base.constaint.ThreadBase中增加了一个属性com.funtester.base.constaint.ThreadBase#progress用于外部访问运行状况。

    HTTP访问接口:

        @GetMapping(value = "/progress")
        public Result progeress() {
            String s = ThreadBase.progress == null ? "没有运行任务" : ThreadBase.progress.runInfo;
            return Result.success(s);
        }
    

    com.funtester.frame.execute.Progress#runInfo格式如下:runInfo = String.format("%s进度:%s %s ,当前QPS: %d", taskDesc, getManyString(ONE, (int) (pro * LENGTH)), getPercent(pro * 100), getQPS());


    FunTester腾讯云年度作者Boss直聘签约作者GDevOps官方合作媒体,非著名测试开发,欢迎关注。

  • 相关阅读:
    休息一下
    把细节放大街上好孕妇有好多
    在银行钱是这样取的
    Word2003表格的AutoFormatType和Style的兼容问题
    [zz]Boost智能指针——shared_ptr
    thrift 安装运行错误 解决
    [zz]boost/shared_ptr 用法总结
    [zz]理解复杂的C/C++声明 const, typedef , 函数指针(转贴)
    [zz]使用thrift做c++,java和python的相互调用
    [zz]Apache Thrift学习小记
  • 原文地址:https://www.cnblogs.com/FunTester/p/14982712.html
Copyright © 2011-2022 走看看