當我遇到10億參數組合
最早提到接口測試的優點時,有一個就是執行效率提升,可能是UI層面執行的N倍。但是今天我要分享的這個案例這個優點的升級版本。某個
最早提到接口測試的優點時,有一個就是執行效率提升,可能是UI層面執行的N倍。但是今天我要分享的這個案例這個優點的升級版本。
某個接口參數倒是不多,但是每個參數的范圍略大,最大的將近500個枚舉范圍,小的也是20個。如果把所有參數組合窮舉完,粗略估計可能10億級別的。
需求就是要把這部分所有參數組合都遍歷進行測試,然后我就開始了踩坑了。
初版方案
一開始的想法就是多個循環嵌套,然后并發發起請求,實現起來非常簡單方便。如下:
@Log4j2nclass TT extends MonitorRT {nn static void main(String[] args) {n ["types參數集合"].each {n def type = itn ["id類型集合"].each {n def id = itn 2.upto(99) {n def a = itn 2.upto(99) {n def b = itn 2.upto(99) {n def c = itn def params = new JSONObject()n params.id = idn params.endTime = 0n params.type = typen params.paramMap = parse("{"a":"${a}","b":"$b","c":"$c"}")n fun {n getHttpResponse(getHttpGet(url,params))n }n }n }n }n }n }n }n}n
但是方案的缺陷顯而易見。
- 數量太大,導致后面的異步任務直接被線程池拒絕
- 無法控制QPS和并發數
針對這第一個問題,我是增加了異步線程池等待隊列的長度,可以我發現了新的問題,就是內存壓力太大,這個會在后面的中也遇到。
升級版
針對存在第二個問題,我回歸到性能測試框架中,通過動態調整QPS的功能來調整QPS或者并發數,這里我選擇了QPS,這個更容易更可控。我的思路是,先把所有參數遍歷一遍,存在一個List當中,然后在去遍歷這個List,通過動態QPS壓測模型把所有請求發出去。
static void main(String[] args) {n def list = []n ["types參數集合"].each {n def type = itn ["id類型集合"].each {n def id = itn 2.upto(99) {n def a = itn 2.upto(99) {n def b = itn 2.upto(99) {n def c = itn def params = new JSONObject()n params.id = idn params.endTime = 0n params.type = typen params.paramMap = parse("{"a":"${a}","b":"$b","c":"$c"}")n }n }n }n }n }n AtomicInteger index = new AtomicInteger()n def test = {n def increment = index.getAndIncrement()n if (increment >= list.size()) FunQpsConcurrent.stop()n else getHttpResponse(getHttpGet(url, list.get(increment)))n }n new FunQpsConcurrent(test,"遍歷10億參數組合").start()n }n
但是新的問題立馬就來了,當我運行改代碼的時候,發現本機的CPU瘋狂飆升,仔細看了一下,原來是GC導致的。存放這么多的數據,內存撐不住了。下面就著手解決內存的問題,這里參考10 億條日志回放chronicle性能測試中的思路。
終版
這里用到了線程安全的隊列java.util.concurrent.LinkedBlockingQueue以及對應長度的等待功能,再配合異步生成請求參數,基本上完美解決需求。這里依舊使用休眠1s來進行緩沖,避免隊列長度過大,只有隊列長度足夠1s的2倍消費即可。
static void main(String[] args) {n def ps = new LinkedBlockingQueue()n fun {n ["types參數集合"].each {n def type = itn ["id類型集合"].each {n def id = itn 2.upto(99) {n def a = itn 2.upto(99) {n def b = itn 2.upto(99) {n def c = itn def params = new JSONObject()n params.id = idn params.endTime = 0n params.type = typen params.paramMap = parse("{"a":"${a}","b":"$b","c":"$c"}")n if (ps.size() > 10_0000) sleep(1.0)n ps.put(params)n }n }n }n }n }n }n AtomicInteger index = new AtomicInteger()n def test = {n def params = ps.poll(100, TimeUnit.MILLISECONDS)n if (params == null) FunQpsConcurrent.stop()n else getHttpResponse(getHttpGet(url, params))n }n new FunQpsConcurrent(test, "遍歷10億參數組合").start()n }n
隨著對隊列的學習和使用,最近自己也想寫一個10億級別的日志回放功能,到時候對比chronicle看看性能如何,敬請期待。
FunTester原創專題推薦~
- 接口功能測試專題
- 性能測試專題
- Groovy專題
- Java、Groovy、Go、Python
- 單測&白盒
- FunTester社群風采
- 測試理論雞湯
- FunTester視頻專題
- 案例分享:方案、BUG、爬蟲
- UI自動化專題
- 測試工具專題
-- By FunTester








