固定QPS压测初试

2020-11-09 09:50:45 浏览数 (1)

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

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

固定QPS实践

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

代码语言:javascript复制
        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()方法即可。

代码语言:javascript复制
    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;
        }


    }

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

旧代码:

代码语言:javascript复制
@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]

这里修改代码如下:

代码语言:javascript复制
        @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

旧代码:

代码语言:javascript复制
 @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()方法之后做的计数增加,可能会导致异步补偿线程判断是否需要补偿,补偿额度出现过度补偿的问题。虽然在较长时间的测试中会慢慢中和掉这个影响,但我还是决定修改掉。

这里修改代码如下:

代码语言:javascript复制
    @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();
        }
    }

0 人点赞