• 固定QPS压测初试


    之前写过一篇固定QPS压测模式探索文章,个人认为这个模型相比固定线程数并发请求压测服务的模型更加贴近实际情况,比较适合做负载测试。在最近的工作中尝试使用固定QPS的压测方案,有了一些实践成果(大部分还是修复了BUG),分享一下。

    先说一下实践代码,然后分享一下自己修复的两个BUG。

    固定QPS实践

    先讲一下最常用的HTTP请求的负载测试实践,跳过单个请求的实践,这里用两个request,通过一个list完成存储,然后创建测试任务。

            def requests = []
            def base = getBase()
            def order = new Order(base)
            order.create(21, 17, "FunTester", "", 1, 1)
            requests << FanLibrary.lastRequest
            order.edit()
            requests << FanLibrary.lastRequest
    
            def threads = []
    
            requests.size().times {
                threads << new RequestTimesFixedQps<>(5, 50, new HeaderMark("requestid"), requests[it])
            }
    
            new FixedQpsConcurrent(threads,"固定QPS实践").start()
    
            allOver()
    

    下面分享一下通用型的内部类实现的测试脚本,只需要实现一下doing()方法和clone()方法即可。

        public static void main(String[] args) {
            TT qps = new TT(5, 50);
            new FixedQpsConcurrent(qps).start();
    
        }
    
        static class TT extends FixedQpsThread{
    
    
            public TT(int i, int i1) {
                super(null, i1, i, null, true);
            }
    
            @Override
            protected void doing() throws Exception {
                sleep(1);
            }
    
            @Override
            public TT clone() {
                return this;
            }
    
    
        }
    

    异步补偿线程的线程池状态验证

    旧代码:

    @Override
            public void run() {
                logger.info("补偿线程开始!");
                while (key) {
                    sleep(HttpClientConstant.LOOP_INTERVAL);
                    int actual = executeTimes.get();
                    int qps = baseThread.qps;
                    long expect = (Time.getTimeStamp() - FixedQpsConcurrent.this.startTime) / 1000 * qps;
                    if (expect > actual + qps) {
                        logger.info("期望执行数:{},实际执行数:{},设置QPS:{}", expect, actual, qps);
                        range((int) expect - actual).forEach(x -> {
                            sleep(100);
                            executorService.execute(threads.get(this.i++ % queueLength).clone());
                        });
                    }
                }
                logger.info("补偿线程结束!");
            }
    

    这里有个问题,在执行补偿的操作时候,线程池可能已经关闭了,可能会导致报错。

    Exception in thread "Thread-1" java.util.concurrent.RejectedExecutionException: Task com.fun.frame.execute.FixedQpsConcurrent$TT@5e6bf970 rejected from java.util.concurrent.ThreadPoolExecutor@15897721[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 50]

    这里修改代码如下:

            @Override
            public void run() {
                logger.info("补偿线程开始!");
                try {
                    while (key) {
                        sleep(HttpClientConstant.LOOP_INTERVAL);
                        int actual = executeTimes.get();
                        int qps = baseThread.qps;
                        long expect = (Time.getTimeStamp() - FixedQpsConcurrent.this.startTime) / 1000 * qps;
                        if (expect > actual + qps) {
                            logger.info("期望执行数:{},实际执行数:{},设置QPS:{}", expect, actual, qps);
                            range((int) expect - actual).forEach(x -> {
                                sleep(100);
                                if (!executorService.isShutdown())
                                    executorService.execute(threads.get(this.i++ % queueLength).clone());
                            });
                        }
                    }
                    logger.info("补偿线程结束!");
                } catch (Exception e) {
                    logger.error("补偿线程发生错误!", e);
                }
            }
    

    增加了executorService.isShutdown()的验证。

    修改计数BUG

    旧代码:

     @Override
        public void run() {
            try {
                before();
                threadmark = this.mark == null ? EMPTY : this.mark.mark(this);
                long s = Time.getTimeStamp();
                doing();
                long e = Time.getTimeStamp();
                long diff = e - s;
                FixedQpsConcurrent.executeTimes.getAndIncrement();
                FixedQpsConcurrent.allTimes.add(diff);
                if (diff > HttpClientConstant.MAX_ACCEPT_TIME)
                    FixedQpsConcurrent.marks.add(diff + CONNECTOR + threadmark);
            } catch (Exception e) {
                FixedQpsConcurrent.errorTimes.getAndIncrement();
                logger.warn("执行任务失败!,标记:{}", threadmark, e);
            } finally {
                after();
            }
        }
    

    由于是在doing()方法之后做的计数增加,可能会导致异步补偿线程判断是否需要补偿,补偿额度出现过度补偿的问题。虽然在较长时间的测试中会慢慢中和掉这个影响,但我还是决定修改掉。

    这里修改代码如下:

        @Override
        public void run() {
            try {
                before();
                threadmark = this.mark == null ? EMPTY : this.mark.mark(this);
                FixedQpsConcurrent.executeTimes.getAndIncrement();
                long s = Time.getTimeStamp();
                doing();
                long e = Time.getTimeStamp();
                long diff = e - s;
                FixedQpsConcurrent.allTimes.add(diff);
                if (diff > HttpClientConstant.MAX_ACCEPT_TIME)
                    FixedQpsConcurrent.marks.add(diff + CONNECTOR + threadmark);
            } catch (Exception e) {
                FixedQpsConcurrent.errorTimes.getAndIncrement();
                logger.warn("执行任务失败!,标记:{}", threadmark, e);
            } finally {
                after();
            }
        }
    

    公众号FunTester首发,原创分享爱好者,腾讯云、开源中国和掘金社区首页推荐,知乎八级强者,欢迎关注、交流,禁止第三方擅自转载。

    FunTester热文精选

  • 相关阅读:
    ZOJ 2702 Unrhymable Rhymes(DP)
    unique() 去重函数
    HDU 4712 Hamming Distance(随机算法)
    HDU 4708 Rotation Lock Puzzle(模拟)
    HBase源代码分析之MemStore的flush发起时机、推断条件等详情(二)
    Androidproject师进阶之路 :《Android开发进阶:从小工到专家》上市啦!
    POJ1062 昂贵的聘礼(最短路)
    easyui required 提交验证
    leetcode
    【翻译自mos文章】在Oracle单机数据库中定义database service
  • 原文地址:https://www.cnblogs.com/FunTester/p/13935440.html
Copyright © 2020-2023  润新知