写点什么

固定 QPS 压测初试

用户头像
FunTester
关注
发布于: 1 小时前

之前写过一篇固定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 热文精选

发布于: 1 小时前阅读数: 2
用户头像

FunTester

关注

公众号:FunTester,650+原创,欢迎关注 2020.10.20 加入

Have Fun,Tester! 公众号FunTester,坚持原创文章的测试人。 FunTester测试框架作者,DCS_FunTester分布式性能测试框架作者。

评论

发布
暂无评论
固定QPS压测初试