性能测试工具 ngrinder 如何控制混合场景压测比例

会飞的猪 · 2019年01月03日 · 最后由 lion 回复于 2022年05月23日 · 3688 次阅读

前言:刚开始用 ngrinder,发现网上混合场景压测的例子太少了,自己去查了很多资料,也去 github 上看介绍,总结出了以下两种方式,现在还有一个问题没有解决。(jython 可以通过导入 py 文件的方式,将多个单接口的测试脚本,导入到一个脚本里进行压测。但是 groovy 我没找到相同的办法,将多个单接口的测试脚本,导入到一个脚本里进行压测,现在只能将多个 test 方法写到一个文件里,显得比较臃肿,希望有此解决办法的大神,留言评论)
一、控制主循环里的比例,如 4 个 test, test1:test2:test3:test4=5:2:1:2 主循环跑 200 次,那么 test1:test2:test3:tesst4 的执行次数比例约等于 5:2:1:2

import net.grinder.scriptengine.groovy.junit.annotation.RunRate
import static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
import net.grinder.plugin.http.HTTPRequest
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.script.GTest
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import HTTPClient.Cookie
import HTTPClient.CookieModule
import HTTPClient.HTTPResponse
import HTTPClient.NVPair

//@Repeat(100)
@RunWith(GrinderRunner)
class TestRunner {

    public static GTest test
    public static HTTPRequest request
    public static NVPair[] headers = []
    public static NVPair[] params = []
    public static Cookie[] cookies = []
    public static GTest test1
    public static GTest test2
    public static GTest test3
    public static GTest test4

    @BeforeProcess
    public static void beforeProcess() {
        HTTPPluginControl.getConnectionDefaults().timeout = 6000
        test1 = new GTest(1, "test1")  // 参数为 ID、显示名
        test2 = new GTest(2, "test2")
        test3 = new GTest(3, "test3")
        test4 = new GTest(4, "test4")
        request = new HTTPRequest()
        grinder.logger.info("before process.");
    }

    @BeforeThread
    public void beforeThread() {
        test1.record(this, "test1")
        test2.record(this, "test2")
        test3.record(this, "test3")
        test4.record(this, "test4")
        grinder.statistics.delayReports=true;
        grinder.logger.info("before thread.");
    }

    @Before
    public void before() {
        request.setHeaders(headers)
        cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) }
        grinder.logger.info("before thread. init headers and cookies");
    }
    @RunRate(50)  // 数字代表百分比
    @Test
    public void test1(){
        HTTPResponse result = request.GET("https://blog.csdn.net/")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
    @RunRate(20)  // 数字代表百分比
    @Test
    public void test2(){
        HTTPResponse result = request.GET("https://www.baidu.com")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
    @RunRate(10)  // 数字代表百分比
    @Test
    public void test3(){
        HTTPResponse result = request.GET("https://testerhome.com")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
    @RunRate(20)  // 数字代表百分比
    @Test
    public void test4(){
        HTTPResponse result = request.GET("http://www.runoob.com")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }

}

数据图标:

二、控制线程比例,如开了 4 个并发,4 个 test, test1:test2:test3:test4 线程比例等于=1:1:1:1

import static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
import net.grinder.plugin.http.HTTPRequest
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.script.GTest
import net.grinder.script.Grinder
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import java.util.Date
import java.util.List
import java.util.ArrayList
import HTTPClient.Cookie
import HTTPClient.CookieModule
import HTTPClient.HTTPResponse
import HTTPClient.NVPair
import TestScriptObjectCalculate

/**
 * @author 乐冉
 */
@RunWith(GrinderRunner)
class TestRunner {

    public static GTest test
    public static HTTPRequest request
    public static NVPair[] headers = []
    public static NVPair[] params = []
    public static Cookie[] cookies = []
    public static GTest test1
    public static GTest test2
    public static GTest test3
    public static GTest test4
    public static TestScriptObjectCalculate  testScript

    @BeforeProcess
    public static void beforeProcess() {
        HTTPPluginControl.getConnectionDefaults().timeout = 6000
        test1 = new GTest(1, "test1")  // 参数为 ID、显示名
        test2 = new GTest(2, "test2")
        test3 = new GTest(3, "test3")
        test4 = new GTest(4, "test4")
        request = new HTTPRequest()
        grinder.logger.info("before process.");
    }

    @BeforeThread
    public void beforeThread() {
        test1.record(this, "test1")
        test2.record(this, "test2")
        test3.record(this, "test3")
        test4.record(this, "test4")
        grinder.statistics.delayReports=true;
        grinder.logger.info("before thread.");
    }

    @Before
    public void before() {
        request.setHeaders(headers)
        cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) }
        grinder.logger.info("before thread. init headers and cookies");
    }

    @Test
    public void test(){
        int n =(int)new java.util.Random().nextInt(100)
        grinder.logger.info(String.valueOf(n))
        //控制线程数
        int tid = grinder.threadNumber
        int s = tid % 4
        switch (s) {
         case 0: 
             this.test1()
             break;
         case 1:          
             this.test2()
             break;
         case 2:          
             this.test3()
             break;
         case 3:          
             this.test4()
             break;
       }
    }

    public void test1(){
        HTTPResponse result = request.GET("https://blog.csdn.net/")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }

    public void test2(){
        HTTPResponse result = request.GET("https://www.baidu.com")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }

    public void test3(){
        HTTPResponse result = request.GET("https://testerhome.com")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }

    public void test4(){
        HTTPResponse result = request.GET("http://www.runoob.com")
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }

}
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 3 条回复 时间 点赞

你的第一种方法是运行次数的百分比,也就是每个请求的运行次数占总次数的百分比。
第二种才是线程数(即虚拟用户)占总线程数百分比,这种在实际模拟真实场景时比较常见,但是你给的示例有点局限性,多个请求是等比例的 1:1:1:1,不过受你这个的启发,我自己尝试出了,多个请求按不同百分比的虚拟用户同时进行请求,不受运行次数限定,而是以运行时长来进行相应的测试。这样才具有实际测试意义。
代码链接:https://www.cnblogs.com/fxcity/p/11239760.html

至于 groovy 引用的方案,我也尝试了一种,但是有点受局限,暂时没有更深入研究。示例如下:
import groovy.lang.GroovyClassLoader
import groovy.lang.GroovyClassLoader.*
//创建一个 calssLoader 对象,解析 class 文件并生成对象,最后调用方法
String scriptFilePath = Thread.currentThread().getContextClassLoader().getResource("./TestImport.groovy").getPath()//获取引用脚本路径
GroovyClassLoader loader = new GroovyClassLoader() // 实例化 GroovyClassLoader 对象
Class groovyClass = loader.parseClass(new File(scriptFilePath)) // 解析待引用的脚本文件
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance() // 实例化 GroovyObject 对象
groovyObject.test(request,requestInfoJson)// // 调用引用脚本文件的 test 方法

问下混合场景,最终报告想看每个接口的响应时间和 TPS,要怎么处理

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册