写点什么

当我遇到 10 亿参数组合

作者:FunTester
  • 2022-10-24
    北京
  • 本文字数:2458 字

    阅读完需:约 8 分钟

最早提到接口测试的优点时,有一个就是执行效率提升,可能是 UI 层面执行的 N 倍。但是今天我要分享的这个案例这个优点的升级版本。


某个接口参数倒是不多,但是每个参数的范围略大,最大的将近 500 个枚举范围,小的也是 20 个。如果把所有参数组合穷举完,粗略估计可能 10 亿级别的。


需求就是要把这部分所有参数组合都遍历进行测试,然后我就开始了踩坑了。

初版方案

一开始的想法就是多个循环嵌套,然后并发发起请求,实现起来非常简单方便。如下:


@Log4j2class TT extends MonitorRT {
static void main(String[] args) { ["types参数集合"].each { def type = it ["id类型集合"].each { def id = it 2.upto(99) { def a = it 2.upto(99) { def b = it 2.upto(99) { def c = it def params = new JSONObject() params.id = id params.endTime = 0 params.type = type params.paramMap = parse("{\"a\":\"${a}\",\"b\":\"$b\",\"c\":\"$c\"}") fun { getHttpResponse(getHttpGet(url,params)) } } } } } } }}
复制代码


但是方案的缺陷显而易见。


  1. 数量太大,导致后面的异步任务直接被线程池拒绝

  2. 无法控制 QPS 和并发数


针对这第一个问题,我是增加了异步线程池等待队列的长度,可以我发现了新的问题,就是内存压力太大,这个会在后面的中也遇到。

升级版

针对存在第二个问题,我回归到性能测试框架中,通过动态调整 QPS 的功能来调整 QPS 或者并发数,这里我选择了 QPS,这个更容易更可控。我的思路是,先把所有参数遍历一遍,存在一个 List 当中,然后在去遍历这个 List,通过动态 QPS 压测模型把所有请求发出去。



static void main(String[] args) { def list = [] ["types参数集合"].each { def type = it ["id类型集合"].each { def id = it 2.upto(99) { def a = it 2.upto(99) { def b = it 2.upto(99) { def c = it def params = new JSONObject() params.id = id params.endTime = 0 params.type = type params.paramMap = parse("{\"a\":\"${a}\",\"b\":\"$b\",\"c\":\"$c\"}") } } } } } AtomicInteger index = new AtomicInteger() def test = { def increment = index.getAndIncrement() if (increment >= list.size()) FunQpsConcurrent.stop() else getHttpResponse(getHttpGet(url, list.get(increment))) } new FunQpsConcurrent(test,"遍历10亿参数组合").start() }
复制代码


但是新的问题立马就来了,当我运行改代码的时候,发现本机的 CPU 疯狂飙升,仔细看了一下,原来是 GC 导致的。存放这么多的数据,内存撑不住了。下面就着手解决内存的问题,这里参考10 亿条日志回放chronicle性能测试中的思路。

终版

这里用到了线程安全的队列java.util.concurrent.LinkedBlockingQueue以及对应长度的等待功能,再配合异步生成请求参数,基本上完美解决需求。这里依旧使用休眠 1s 来进行缓冲,避免队列长度过大,只有队列长度足够 1s 的 2 倍消费即可。


    static void main(String[] args) {        def ps = new LinkedBlockingQueue()        fun {            ["types参数集合"].each {                def type = it                ["id类型集合"].each {                    def id = it                    2.upto(99) {                        def a = it                        2.upto(99) {                            def b = it                            2.upto(99) {                                def c = it                                def params = new JSONObject()                                params.id = id                                params.endTime = 0                                params.type = type                                params.paramMap = parse("{\"a\":\"${a}\",\"b\":\"$b\",\"c\":\"$c\"}")                                if (ps.size() > 10_0000) sleep(1.0)                                ps.put(params)                            }                        }                    }                }            }        }        AtomicInteger index = new AtomicInteger()        def test = {            def params = ps.poll(100, TimeUnit.MILLISECONDS)            if (params == null) FunQpsConcurrent.stop()            else getHttpResponse(getHttpGet(url, params))        }        new FunQpsConcurrent(test, "遍历10亿参数组合").start()    }
复制代码


随着对队列的学习和使用,最近自己也想写一个 10 亿级别的日志回放功能,到时候对比chronicle看看性能如何,敬请期待。


发布于: 刚刚阅读数: 3
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
当我遇到10亿参数组合_FunTester_InfoQ写作社区