• 高QPS下的固定QPS模型


    在之前写过的文章固定QPS压测模式探索固定QPS压测初试中,我用到了一个任务发生器和sleep()方法来达到固定QPS的请求实现。但是在最近的工作中,在高QPS场景下,这种方式就会显示出其缺点:单线程任务生成器性能不足,由此带来的副作用就是误差较大。为此,我引入了多线程任务生成器的功能。

    主要思路就是在性能测试软启动完成后,根据设置QPS大小分配多个的线程来完成生成任务的功能。

    这里引入一个常量:

        /**
         * 单个线程执行的最大QPS任务速率
         */
        public static int QPS_PER_THREAD = 250;
    

    固定QPS测试用例启动方式改成如下:

        /**
         * 执行多线程任务
         * 默认取list中thread对象,丢入线程池,完成多线程执行,如果没有threadname,name默认采用desc+线程数作为threadname,去除末尾的日期
         */
        public PerformanceResultBean start() {
            boolean isTimesMode = baseThread.isTimesMode;
            int limit = baseThread.limit;
            int qps = baseThread.qps;
            executeThread = qps / Constant.QPS_PER_THREAD + 1;
            interval = 1_000_000_000 / qps;//此处单位1s=1000ms,1ms=1000000ns
            int runupTotal = qps * PREFIX_RUN;//计算总的请求量
            double diffTime = 2 * (Constant.RUNUP_TIME / PREFIX_RUN * interval - interval);//计算最大时间间隔和最小时间间隔差值
            double piece = diffTime / runupTotal;//计算每一次请求时间增量
            for (int i = runupTotal; i > 0; i--) {
                executorService.execute(threads.get(limit-- % queueLength).clone());
                sleep((long) (interval + i * piece));
            }
            sleep(1.0);
            allTimes = new Vector<>();
            marks = new Vector<>();
            executeTimes.getAndSet(0);
            errorTimes.getAndSet(0);
            logger.info("=========预热完成,开始测试!=========");
            Progress progress = new Progress(threads, StatisticsUtil.getTrueName(desc), executeTimes);
            new Thread(progress).start();
            startTime = Time.getTimeStamp();
            AidThread aidThread = new AidThread();
            new Thread(aidThread).start();
            CountDownLatch countDownLatch = new CountDownLatch(executeThread);
            for (int i = 0; i < executeThread; i++) {
                new FunTester(countDownLatch).start();
            }
            endTime = Time.getTimeStamp();
            aidThread.stop();
            progress.stop();
            GCThread.stop();
            try {
                countDownLatch.wait();
                executorService.shutdown();
                executorService.awaitTermination(HttpClientConstant.WAIT_TERMINATION_TIMEOUT, TimeUnit.SECONDS);//此方法需要在shutdown方法执行之后执行
            } catch (InterruptedException e) {
                logger.error("线程池等待任务结束失败!", e);
            }
            logger.info("总计执行 {} ,共用时:{} s,执行总数:{},错误数:{}!", baseThread.isTimesMode ? baseThread.limit + "次任务" : "秒", Time.getTimeDiffer(startTime, endTime), executeTimes, errorTimes);
            return over();
        }
    

    其中第二个for循环正是启动多个线程执行测试:

            for (int i = 0; i < executeThread; i++) {
                new FunTester(countDownLatch).start();
            }
    

    内部多线程类com.funtester.frame.execute.FixedQpsConcurrent.FunTester的代码如下:

        /**
         * 执行请求生成和执行类
         */
        private class FunTester extends Thread {
    
            FunTester(CountDownLatch countDownLatch) {
                this.countDownLatch = countDownLatch;
            }
    
            CountDownLatch countDownLatch;
    
            boolean isTimesMode = baseThread.isTimesMode;
    
            int limit = baseThread.limit / executeThread;
    
            long nanosec = interval * executeThread;
    
            @Override
            public void run() {
                try {
                    while (true) {
                        executorService.execute(threads.get(limit-- % queueLength).clone());
                        if (needAbord || (isTimesMode ? limit < 1 : Time.getTimeStamp() - startTime > limit)) break;
                        SourceCode.sleep(nanosec);
                    }
                } catch (Exception e) {
                    logger.warn("任务发生器执行发生错误了!", e);
                } finally {
                    countDownLatch.countDown();
                }
            }
    
        }
    
    

    目前在1-2万的QPS场景下,测试结果比较满意,先就这么地吧,以后碰到坑了,再继续优化。


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

  • 相关阅读:
    开涛spring3(6.5)
    开涛spring3(6.4)
    开涛spring3(6.3)
    开涛spring3(6.2)
    开涛spring3(6.1)
    开涛spring3(5.4)
    开涛spring3(5.3)
    开涛spring3(5.1&5.2)
    开涛spring3(4.4)
    开涛spring3(4.3)
  • 原文地址:https://www.cnblogs.com/FunTester/p/14821972.html
Copyright © 2020-2023  润新知