• i++需要多少QPS才能测出BUG


    前段时间我关注了一篇文章,分享如果使用并发压测发现 BUG。突然想起一个话题:

    线程不安全需要多少 QPS 压测才能发现 BUG ?

    我接触到的并发缺陷绝大部分是因为线程安全问题导致的,还有一些数据库锁的问题(这个不擅长)这里就不分享了。

    关于 Java 的一些线程安全的问题,可以参考旧文:

    下面我们来聊聊上面提到的问题,因为这涉及到不同类型的 BUG 需要多少 QPS 才能测出来 BUG,今天来分享一下最简单的线程不安全操作i++需要多少 QPS 才能测出来BUG。

    用例设计思路

    首先,我使用的同一个 JVM 来测试i++,发现极容易出现 BUG,后来放弃了这种方式。经过思考发现如果放在一个 JVM 里面,本身已经创建了很多线程去执行i++,这种跟实际接口测试差异比我想象的大很多。
    其次,我创建了一个简单的 Springboot 项目,写一个简单的接口来实现。

    总提测下来,上面的问题需要修正,因为能不能测出来不是一个 Boolean 值,而是一个概率值,后面我也会用发现比例值来表示是测出 BUG 的难易程度。

    服务端设计

    之前一直用moco_funtester框架来构建服务端不行了,无法动态接口返回。所以只能简单弄一个 Springboot 项目。其他的就不分享了,只分享一下 controller 的部分。这里模拟盘了一个接口平均响应时间 10ms,然后执行一个非线程安全的操作。

        int i;
    
        @GetMapping(value = "/funtest")
        public Result test1() {
           Thread.sleep(SourceCode.getRandomInt(20));
            return Result.success(i++);
        }
    
        @GetMapping(value = "/geti")
        public Result test() {
            return Result.success(i);
        }
        @GetMapping(value = "/zero")
        public Result te2st() {
            i = 0;
            return Result.success(i);
        }
    

    测试用例

    这里没有使用正经的测试框架,只用了异步线程池和粗略的sleep休眠的方法控制 QPS,所以这里会有一个实际 QPS 统计。

    1. 默认以固定 QPS 执行 20s。
    2. 先重置,后执行,最后获取结果。
    3. 取消所有日志打印,避免误差
    4. 统计误差数量和误差比

    测试用例模拟盘两个模型:线程模型和 QPS 模型。

    线程模型用例

        public static void main(String[] args) {
            def test = {getHttpResponse(getHttpGet("http://localhost:8080/user/funtest"))}
            def get = {getHttpResponse(getHttpGet("http://localhost:8080/user/geti"))}
            def init = {getHttpResponse(getHttpGet("http://localhost:8080/user/zero"))}
            AtomicInteger index = new AtomicInteger()
            FunHttp.LOG_KEY = false
            def t = 1000
            def size = 1
            setPoolMax(500)
            init()
            fun {
                output(DEFAULT_STRING)
            }
            sleep(1.0)
            def start = Time.getTimeStamp()
            size.times {
                fun {
                    t.times {
                        test()
                        index.getAndIncrement()
                    }
                }
            }
            ThreadPoolUtil.waitFunIdle()
            def value = get().getIntValue("data")
            def end = Time.getTimeStamp()
            output("当前 QPS: ${index / (end - start) * 1000}")
            output(index.get(), value)
            output(getPercent(index.get(), index.get() - value))
        }
    
    

    QPS 模型用例

        public static void main(String[] args) {
            def test = {getHttpResponse(getHttpGet("http://localhost:8080/user/funtest"))}
            def get = {getHttpResponse(getHttpGet("http://localhost:8080/user/geti"))}
            def init = {getHttpResponse(getHttpGet("http://localhost:8080/user/zero"))}
            AtomicInteger index = new AtomicInteger()
            FunHttp.LOG_KEY = false
            def qps = 100
            def t = qps * 10
            setPoolMax(1000)
            init()
            def decimal = 1_000_000_000 / qps
            fun {
                output(decimal)
            }
            sleep(1.0)
            def start = Time.getTimeStamp()
            t.times {
                sleepNano(decimal as long)
                fun {
                    test()
                    index.getAndIncrement()
                }
            }
            ThreadPoolUtil.waitFunIdle()
            def value = get().getIntValue("data")
            def end = Time.getTimeStamp()
            output("当前 QPS: ${index / (end - start) * 1000}")
            output(index.get(), value)
            output(getPercent(index.get(), index.get() - value))
        }
    

    测试结果

    线程模型

    线程模型模仿的固定线程数去不断请求接口,这里由于接口平均响应时间 10ms,每个线程执行次数设计为 2000 次,差不多 20s 执行完。

    设计 QPS 实际 QPS 误差数量 误差比(百分比)
    10 9.6 0 0
    20 18.7 0 0
    50 42 0 0
    100 87 2 0.1
    200 174 9 0.22
    300 280 18 0.3
    400 285 23 0.28
    500 417 2 0.02

    QPS 模型

    由于对实际结果测试并不能很好预期,这里就先从较小的 QPS 开始了。全程没有触发性能瓶颈,误差部分,测 5 次,取误差最大的一次记录。这里模拟的线程模型的

    线程数 实际 QPS 误差数量 误差比(百分比)
    1 76 0 0
    2 144 1 0.05
    4 305 33 0.41
    8 617 111 0.69
    12 927 224 0.93

    经过上面测试,对于需要多少压力才能发现可能存在的缺陷,希望本文能投提供参考。

  • 相关阅读:
    利用Python 统计txt 文档词频 次数
    Markdown工具Typora结合gitee码云图床自动上传云端图片
    主数据MDM下发eHR系统操作整理v1.0
    Python读取Excel数据生成图表 v2.0
    HTML基础(20200610)
    Python网络编程socket 简易聊天窗
    selenium-python-验证码-动态验证码
    selenium-python-验证码-滑动验证码
    Linux系统学习 20200506
    @Mapper @Insert 注解式方法批量入库(ORACLE数据库)
  • 原文地址:https://www.cnblogs.com/FunTester/p/16657165.html
Copyright © 2020-2023  润新知