Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

基于nGrinder进行负载测试

5
Mar
2019

基于nGrinder进行负载测试

By Alex
/ in Test
/ tags Groovy
0 Comments
简介

nGrinder是一个基于Grinder的压力测试平台。在此平台上你可以创建测试脚本、执行测试、监控目标服务器,同步的生成测试结果。

架构
基础组件

nGrinder由两种关键组件:

  1. 控制器:提供性能测试的Web接口,支持协调测试进程、收集并展示测试相关的统计信息,允许用户创建或修改测试脚本
  2. 代理,可以运行在两种模式下:
    1. Agent:运行线程、进程,将负载施加到目标服务器上
    2. Monitor:监控目标服务器的性能,例如CPU/内存

代理启动后会尝试连接到控制器,然后被添加到AgentControllerServer —— 类似于Agent的池中。当用户发起一个性能测试后,nGrinder会创建一个协调Agents的SingleConsole(和Grinder中的Console作区分,起名叫SingleConsole),所需数量的Agent从AgentControllerServer交付给SingleConsole管理,SingleConsole向Agents发送测试脚本、测试资源,并控制测试流,直到测试完毕。测试完毕后,Agents回到AgentControllerServer的Agent池中,供后续测试重用。SingleConsole则回到ConsoleManager中。

nGrinder和Grinder最大的不同是,前者在控制器中持有多个Console实例、Agents。Console之间是相互独立的,并且可以并行的运行。Agent可以被预先分配给Console,也可以根据需要随时被请求、分配。nGrinder力求对Agent机器的最大化利用。其它测试平台,常常为测试任务预先分配Agents。用户为了争抢资源,常常会提前申请Agent而不充分利用。nGrinder仅在测试运行时才真正动态分配Agent。

架构图

ngrinder-architecture-0

集群架构

从3.1版本开始nGrinder开始支持控制器集群。性能测试可以被集群中的某个控制器调度,可以在多组Agent(称为Region)上执行。

ngrinder-architecture-clustering

 

上图的架构中,包含两个控制器组成的集群:

  1. 所有控制器共享数据库、网络文件系统。所有控制器的 ${NGRINDER_HOME}必须指向同一NFS路径
  2. 每个控制器可以有自己独特的属性、日志输出,这些信息存放在 ${NGRINDER_EX_HOME}
  3. 所有控制器同步EhCache缓存中的共享数据

每个nGrinder控制器实例都可以独立的服务用户,需要注意的是nGrinder没有提供分布式会话,因此切换控制器后你需要重新登陆。

用户接口
快速开始

在Quick Start中输入一个URL,可以自动生成一个测试脚本。测试脚本就是一段以Groovy语言编写的JUnit测试。

测试配置

在Home页面点击Performance Test链接,可以进入已经定义的性能测试的列表。

点击列表项,可以进入单个性能测试的详情页面。其第一个选项卡是Test Configuration:

配置项 说明
Agent 使用的代理数量
Vuser per agent

每个代理的“虚拟用户”的数量,可以展开设置线程、进程数量

总线程数 = 线程*进程 = 虚拟用户数 

和其它测试工具有所不同,nGrinder不去模拟真实的用户行为(比如说模拟10000个用户,在那每隔几秒发一个请求),而是专注于如何达到服务器的能力限制

如果你的确需要模拟真实用户行为,例如需要10000个虚拟用户, 你可以:

  1. 准备两台性能较好的代理机器,例如2核心/6GB
  2. 编写Groovy脚本(比Jyhton更高效)
  3. 在system.conf配置 agent.max.vuser = 5000,解除默认3000的最大限制
  4. 调用 grinder.sleep(how_much_sleep_in_milliseconds)模拟用户思考导致的操作延迟
  5. 配置10进程 * 500线程

如果代理机器的内存较低,例如4GB,则10个或更多进程会导致swap进而影响性能

Region 使用哪个区域中的Agent/Console
Target Host

被压测的主机,应当和脚本中所访问的主机一致。nGrinder控制器会在测试开始后,对Target host进行监控,你可以在图表中看到Target host的性能指标

你可以使用如下的形式来覆盖默认DNS解析:

1
www.gmem.cc:39.107.94.255 
Duration 测试执行多久
Run Count 测试运行多少次
Enable Ramp-Up 在每个间隔之后增加进程数量
  Initial Count 启动测试时工作进程的数量
  Incremental Step 每个间隔增加进程的数量
  Initial Sleep Time 最初休眠的时间
  Interval 每个间隔的区间
Parameter 能够向测试传递单个名为param的参数,填写此参数的值
脚本管理

在Home页面点击Script链接,可以进入已经定义的脚本的列表。

nGrinder内置了一个SVN服务器,用于管理用户编写的脚本文件以及资源,每个用户由独立的SVN存储库。你可以通过SVN客户端签出并管理脚本和资源,也可以通过nGrinder的UI来添加、删除、上传脚本或资源文件或目录。

Groovy测试脚本

从3.2版本开始,nGrinder在Jython的基础上支持Groovy作为脚本编写语言。 

Groovy脚本是基于Junit的,因此对熟悉Java单元测试的人很友好。你可以通过SVN版本库管理压测脚本,甚至使用Maven来管理压测脚本的项目、在IDE中执行运行、调试压测脚本。

脚本结构
脚本模板

下面是一个基本的模板:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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.script.GTest
import net.grinder.script.Grinder
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
 
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
 
import HTTPClient.HTTPResponse
 
// 测试用例必须附加如下注解
@RunWith(GrinderRunner)
class Test {
 
    public static GTest test;
    public static HTTPRequest request;
 
    // 每个进程被创建之前,执行的方法
    // 类似的还有@AfterProcess
    @BeforeProcess
    public static void beforeClass() {
        // 创建一个GTest实例
        test = new GTest(1, "${name}");
        // 录制对此HTTPRequest的任何调用,每次对它的调用都会增加TPS
        request = new HTTPRequest();
        test.record(request);
        grinder.logger.info("before process.");
 
        // 如果需要录制多个HTTPRequest,需要使用多个名字不同的GTest实例
    }
 
    // 每个线程被创建之前,执行的方法
    // 类似的还有@AfterThread
    @BeforeThread
    public void beforeThread() {
        grinder.statistics.delayReports=true;
        grinder.logger.info("before thread.");
    }
 
    // 在停止测试之前,带有这个注解的方法会被持续不断的运行
    @Test
    public void test(){
        HTTPResponse result = request.GET("${url}");
        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));
        }
    }
}
特殊注解
注解 说明
@BeforeProcess

Groovy脚本引擎创建新进程后执行的逻辑,例如:

  1. 加载所有线程共享的静态资源
  2. 定义GTest并instrument测试目标
@AfterProcess

Groovy脚本引擎在进程被终结后执行的逻辑,例如:

  1. 关闭资源文件
@BeforeThread

在进程的每个测试线程执行之前执行的逻辑,例如:

  1. 登陆到目标系统
  2. 准备线程绑定变量
@AfterThread

在进程的每个测试线程执行之后执行的逻辑,例如:

  1. 登出系统
@Before 每次测试之前、之后执行的逻辑
@After
@Test

需要执行的测试本身,会反复调用

你可以为多个方法添加此注解,但是需要注意,和JUnit不同,nGrinder会让所有这些方法共享实例变量

代码片断
向脚本传递参数

在Test Configuration中指定的单个参数,可以这样获取:

Java
1
2
// 名字固定为param
System.getProperty("param", "defaultValue")

实际上,上述param参数是在命令行通过 -Dparam=value传递给测试进程的。 

引入Maven依赖ngrinder-groovy,可以调用net.grinder.util.GrinderUtils定义的静态方法,获取参数并进行类型转换。

上下文信息
Java
1
2
3
4
5
6
processnumber=grinder.processNumber                                 //获取当前的进程号,应该会相应返回0,1,2,3,4
totalprocess=grinder.getProperties().getInt("grinder.processes", 1) //获取测试的进程总数,返回5
threadnumber=grinder.threadNumber                                   //获取当前的线程号,应该会相应返回0,1
totalthread=grinder.getProperties().getInt("grinder.threads", 1)    //获取测试的线程总数,应该返回2
run=grinder.runNumber;                                              //获取当前的测试号,应该相应返回1,2,3
totalrun=grinder.getProperties().getInt("grinder.runs", 1);         //返回测试的总数,如3
HTTP插件参数
Java
1
HTTPPluginControl.getConnectionDefaults().timeout = 6000 
POST请求
Java
1
2
request = new HTTPRequest();
request.POST("http://www.google.com", [new NVPair("key1","value1"), new NVPair("key2":"value2")] as NVPair[])
响应断言
Java
1
2
3
def result = request.GET("http://www.google.co.kr")
assertThat(result.statusCode, is(200)) // 状态码
assertThat(result.text, containsString("google")) // 响应体
录制测试方法

而不是录制HTTPRequest调用:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RunWith(GrinderRunner)
class TestRunner {
    static GTest test1
    static GTest test2
    @BeforeProcess
    public static void beforeProcess() {
        test1 = new GTest(1,"test1_statistics")
        test2 = new GTest(2,"test2_statistics")
    }
    @BeforeThread
    public void beforeThread() {
        test1.record(this, "doTransaction1"); // 录制当前对象的doTransaction1调用到测试test1
        test2.record(this, "doTransaction2"); // 录制当前对象的doTransaction2调用到测试test2
        grinder.statistics.delayReports=true;
    }
    @Test
    public void doTransaction1(){
    }
    @Test
    public void doTransaction2(){
    }
}

被录制方法的执行耗时会影响测试报告。

设置权重

你可以设置每个测试方法,占用总计的测试次数的比例:

Java
1
2
3
4
5
6
7
8
9
@RunRate(50)  // 此方法占所有测试次数(runs)的50%
@Test
public void test1() {
}
 
@RunRate(20)
@Test
public void test2() {
}

如果总和不到100,则会有一定的测试次数(runs)什么都不干。

调整日志级别
Java
1
LoggerFactory.getLogger("worker").setLevel(Level.ERROR)
解析JSON
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import groovy.json.JsonSlurper
 
def message = """
{
  "glossary": {
     "title": "example glossary",
      "GlossDiv": {
           "title": "S"
         }
      }
  }
}"""
 
def jsonMsg = new JsonSlurper().parseText(message)
grinder.logger.info(jsonMsg.glossary.title)
解析XML 
Java
1
def hello = new XmlParser().parseText(message);
流式处理大响应
Java
1
2
3
4
5
6
7
8
byte[] buffer = new byte[1000];
 
HTTPResponse result = request.GET("http://www.google.com");
 
// 只读取1000字节就结束
def stream = result.getInputStream();
stream.read(buffer);
stream.close();
按测试线程决定行为
Java
1
2
3
4
5
6
7
8
9
10
def threadId;
@BeforeThread
public void beforeThread() {
  threadId = GrinderUtils.threadUniqId;
}
 
@Test
public doTest() {
   // 根据threadId进行条件分支
}
使用外部库

有两种方式:

  1. 在测试脚本所在目录下,建立lib目录,其中存放jar
  2. 使用Maven依赖管理机制 
使用资源文件
  1. 在测试脚本所在目录下,建立resources目录,其中存放资源文件,然后参考下面的脚本加载资源:
    Java
    1
    2
    3
    4
    @BeforeProcess
    public static void beforeProcess() {
        text = new File("./resources/resource1.txt").text;
    } 
  2. 使用Maven,从类路径下加载:
    Java
    1
    ReflectionUtils.getCallingClass(0).getResourceAsStream("/resource1.txt");
REST API

nGrinder 3.3提供了超过40个REST API,使用这些REST API你可以构建自己的nGrinder前端,或者让其它工具和nGrinder进行集成。REST API的功能包括:

  1. 用户管理
  2. 代理管理
  3. 脚本管理
  4. 性能测试管理
  5. 系统管理

REST API使用HTTP基本身份验证。

示例
Shell
1
2
3
4
5
6
7
8
9
10
# 克隆现有性能测试,然后启动,226为测试ID
curl -u admin:admin http://ngrinder-host/perftest/api/226/clone_and_start
# 返回JSON形式、新创建的测试
# 你可以传递agentCount、scriptRevision参数来指定代理数量、脚本的修订版
 
# 查看现有的性能测试
curl -u admin:admin http://ngrinder-host/perftest/api/226
 
# 查看性能测试列表
curl -u admin:admin http://ngrinder-host/perftest/api
接口形式

对于nGrinder UI的URL(其URL Path称为topic),后缀以/api通常就是其对应的REST API端点:

URL 动词 描述
http://ngrinder-host/{topic}/api/ GET 获取元素列表
http://ngrinder-host/{topic}/api?ids={id,id} GET 获取多个元素
http://ngrinder-host/{topic}/api/{kind}?ids={id,id} GET 获得多个元素的某个特定信息
http://ngrinder-host/{topic}/api/{id} GET 获得一个元素
http://ngrinder-host/{topic}/api/{id}/{kind} GET 获取一个元素的某个特定信息
http://ngrinder-host/{topic}/api/ POST 新建元素
http://ngrinder-host/{topic}/api/{id} PUT 修改一个元素
http://ngrinder-host/{topic}/api?ids={id,id} PUT 修改多个元素
http://ngrinder-host/{topic}/api?action={action}&ids={id,id} PUT 指定若干元素上执行动作
http://ngrinder-host/{topic}/api/{id}?action={action} PUT 在指定的元素上执行动作
http://ngrinder-host/{topic}/api/{id} DELETE 删除一个元素
http://ngrinder-host/{topic}/api?ids={id,id} DELETE 删除多个元素
接口列表
PerfTest

参考:https://github.com/naver/ngrinder/wiki/REST-API-PerfTest

← Flannel学习笔记
2019年3月杭州 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 基于Eclipse和Maven的Groovy开发
  • Groovy学习笔记
  • 使用Mockito进行单元测试
  • Go语言单元测试和仿冒
  • 基于Spring Test和Mockito进行单元测试

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • Bazel学习笔记 38 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2