技术分享 | JMeter性能测试实现与分析

导语

JMeter是由著名开源软件巨头Apache组织开发的纯Java的压力测试工具,它即能测试动态服务(WebService),也能测试静态资源,包括Servlet服务、CGI脚本等,还能测试动态语言服务(PHP、Java、ASP.NET等)和数据库和FTP服务,测试结束之后能够根据测试的脚本配置和服务器配置情况,生成可视化的测试报告图表。本文以测试基于HTTP请求的WebService服务为例进行讲解。

1. 环境与配置

下载安装

JMeter的使用十分简单,下载之后,如果是Windows系统,运行bin目录中的jmeter.bat即可,如果是UNIX-like系统,运行jmeter.sh即可。

另外,JMeter还支持以命令行方式运行。一般的命令方法是这样的:

jmeter.sh -n -t test_plan.jmx -l output_log.jtl

-n 通知JMeter以非GUI方式运行
-t 指定将要运行的测试计划,测试计划本质上一个XML文件
-l 指定测试结果集输出文件,这个文件本质上是一个CSV文件
如果以命令行方式运行,需要修改bin目录中的jmeter.properties文件中的jmeterengine.force.system.exit这个选项,原因是以命令行方式运行时是无法对GUI窗口执行关闭操作的,这会导致UI线程永远都不会退出,这种线程死锁的情况会被JMeter认为是异常而报错。

JMeter运行时会创建日志,对于未开启UAC的Windows用户,操作没有任何影响,日志文件是保存在bin目录中的,也就是说是和jmeter.bat文件保存在一起的,可是对已经开启了UAC的用户来说,如果选择以管理员权限运行jmeter.bat,并且启动了凭证验证机制,那么日志文件将不会与jmeter.bat保存在一起,因为Windows下的bat文件是被cmd.exe(命令提示符,Windows的命令行Shell程序)所解析和执行的,而cmd.exe这个程序是在C:\Windows\System32这个目录下的(我们假定你的系统安装在C盘),所以启动程序的进程上下文环境发生了变化,所以日志文件也会保存在C:\Windows\System32这个目录中,更严重的,在某些情况下会因为CLASSPATH配置与JMeter运行环境出现不一致而导致找不到相应的Jar包,出现java.lang.ClassNotFoundException。事实上这个问题是可以被处理的,所以,可以认为这是jmeter.bat程序的一个BUG。

关于什么是UAC,官方有详细介绍,请点击这里查看,点击这里还可以看到一个更简单的介绍和说明。

环境要求

JMeter 3.0于今年5月中旬发布,与之前的版本不同,这个版本明确要求JDK 1.7以上版本的支持,推荐使用JDK1.8版本,并且这个版本的JMeter中使用的Apache HttpClient库的版本也已经更新到了4.5.2。

如果用户使用JMeter 2.10或者更早的版本,那么JMeter中集成的是HttpClient库的3.1甚至更低的版本,此时如果被测试的Java请求是使用HttpClient库的4.5及以上版本编译的,那么运行时会出现莫名其妙的网络错误,这是因为HttpClient库的版本不兼容造成的,解决方法是在自己引用的Java请求中使用HttpClient3.1版本,和JMeter保持一致。然而推荐的方式是改用JMeter3.0以上版本,因为官方已经明确声明自JMeter3.0起,HttpClient3.1的版本兼容性支持将被废弃。

JMeter3.0以上版本提供了许多新功能,修复了性能上的几个问题,而且HttpClient4.5以上版本也改进了一些功能,修复了退出时持久连接未关闭的BUG,改进了对HTTPS的支持。

2. 执行测试

几个概念

测试计划,也就是Test Plan,是整个测试任务管理的顶级单位,所有的对于测试任务的配置,都是放在测试计划中的。这就好像一个根目录一样,所有的文件和数据,都是保存到这个根目录之下的。

线程组,也就是Thread Group,是可以被独立调试、执行和调度的独立单元,一个测试计划中可以有多个线程组,每个线程组可以配置并发用户数、运行次数、并发加压强度、定时任务等参数。

取样器,也就是Sampler,是一个HTTP请求的所有逻辑关系承载,在这一个取样器中,可以配置请求目标服务器的主域名、请求路径、参数、请求头信息、客户端Cookie策略等。

监听器,也就是Listener,是对测试过程和测试数据的收集机制,最常用的是聚合报告(Aggregate Report)和
逻辑控制器,也就是Logic Controller,在这里可以配置多个取样器的控制逻辑,比如我们通常都会有登录服务只访问一次,而数据服务或者查询服务要访问多次,此时可以通过只访问一次登录接口,然后根据需求对数据服务或者查询服务进行压力测试的方式完成。

配置元件,也就是Config Element,这里最常用的功能,是对HTTP请求的DNS策略、缓存策略、Cookies策略、Header信息、认证机制等信息进行配置,对于需要实现请求参数化的,还可以使用CSV Data Set Config功能。

配置任务

创建一个线程组,在TestPlan上点击右键,选择Add->Threads(Users)->Thread Group即可完成一个线程组的创建。

并发数:在Number of Threads(users)中设置,设置更多的用户将需要更多的内存,一般情况下,如果并发数设置为1000,循环数设置为10,运行时需要的内存将会再700MB到1GB左右,在此数值之上时需要对JVM进行调优,否则有可能因为JVM或者测试机器性能问题导致测试数据不准确,甚至有可能导致JMeter无响应造成任务失败。

集合点:在Ramp-Up Period(in seconds)中设置,这里可以控制加压强度,这个设置项的意义是多少秒之内完成所有用户的加压,在此处设置一个恰当的值,再勾选设置项Scheduler,然后设置其中的Startup delay(seconds)选项即可完成集合点的设置。从而达到每n个用户一起并发请求这样的效果。

循环数:设置Loop Count即可,如果勾选了Forever,就是永远运行,除非用户手动停止。如果是在命令行上运行的测试计划,并且启用了这个选项,只能使用CTRL+C强行中止,如果任务是被使用类似于nohup这样的方式运行在后台的,只能强制杀死进程完成退出操作,不过这种方式下不用担心测试数据丢失,因为JMeter对测试数据的输出和保存是实时的。

检查点:设置检查点之前,需要先为线程组添加一个取样器,我们以HTTP Sampler为例,在线程组上点击右键,选择Add->Sampler->HTTP Request即可,取样器添加完成之后,需要先对此取样器设置主域名,访问路径,内容编码,超时时间、提交参数等诸多信息和参数。配置完成之后,在此取样器上点击右键,选择Add->Assertion->Response Assertion,然后对这个响应断言进行设置,它支持Contains(包含)、Matches(匹配)、Substring(子字符串)、Not(非运算)四种方式。特别的,我们不需要对Patterns to Test中的内容进行字符转义。

参数化:待完善。

配置其他测试选项,最常用的其他测试选项,是HTTP Request的头信息,通过这个功能可以设置HTTP请求提交时的Header信息和参数,设置方法是在取样器上点击Add->Config Element->HTTP Header Manager,然后就可以添加一个HTTP头信息管理器,然后添加若干参数用于提交HTTP请求时提交到服务器。

执行测试

单个执行:在线程组上,点击右键,选择Start,即可按照此线程组设置的并发数的循环数启动此线程组进行测试。

验证执行:这个功能是JMeter3.0之后才有的功能,在线程组上,点击右键,选择Validate即可运行一次,无论并发数和循环数是多少,只运行一次,可以在修改参数之后快速验证此线程组是否正确。

部分执行:当一个测试计划中有多个线程组的时候,可以按住CTRL键单个选择或取消选择,也可以按住SHIFT键连续选择多个线程组,选择了自己想要执行的线程组之后,在被选择的多个线程组的任意一个上点击右键,选择Start即可执行被选择的线程组,这些线程组会按设置的所有参数立即启动。

全量执行:在测试计划上点击左键选中它,然后点击命令栏的Start按钮,或者菜单Run->Start命令,或者按下CTRL+R快捷键,都会立即启动整个测试计划下的所有线程组开始执行测试任务。

服务器监控

先介绍几个最常用的服务器状态监控工具:
top Linux自带的经典的系统性能情况查看工具

htop 这是一个第三方工具,用C语言写成,需要下载、编译和安装,是top命令的改进版本,功能十分强大,视觉效果很好
glances,这是类似于htop的一个工具,使用Python写成,需要Python3.4以上版本的支持,通过Python的pip3即可一键完成安装,和htop一样,功能强大、视觉效果很好
free Linux自带的命令,用于查看内存状态
vmstat Linux组件,用于快速查看系统的性能状态信息
nmon 这是一个第三方工具,绿色免安装,使用C语言写成,它的原理是轮询和收集/proc下的系统运行信息,然后进行分析整理,所以需要nmonChart的配合,可以生成十分直观和漂亮的性能图表,并且通过配置可以实现动态分析和在线查看。老版本的nmon会生成一个带有VB宏的Excel文档,然后下载下来之后必须使用Excel打开并且必须启用宏功能,这种方式既不安全也很麻烦,已经被淘汰了。

iftop 这是一个第三方工具,需要下载并且编译安装才能使用,iftop工具最新版本0.17的安装文件中对依赖包没有处理,所以如果编译失败,需要我们手动检查libpcap、libpcap-devel、ncurses、ncurses-devel这几个依赖包是否正确安装,安装完毕直接输入iftop即可启动,可以非常直观地看到网络设备的连接速度情况。

分析结果

基础分析和查看,要想快速查看测试任务执行情况,需要对每一个取样器都配置结果树和断言结果,操作方法很简单,在取样器上点击右键,选择Add->Listener->Assertion Result即可添加一个断言结果,这种情况下对每一次请求结果的断言检查是否成功,会立即看到,当然,如果断言失败,这条测试用例也就失败了。添加结果树的方法同样简单,在取样器上点击右键,选择Add->Listner->View Results Tree即可,添加结果查看器的好处很多,可以看到取样器的每一次请求的提交参数、时间等所有信息以及响应结果,便于深入细致的分析测试执行情况和成功与失败的原因。
综合查看,综合查看结果,需要添加聚合报告,操作方法是在线程组中点击右键,选择Add->Listner->Aggregate Report,添加聚合报告的好处是能够查看到此线程组下的所有取样器的执行次数,平时时间、中值时间、最大时间、最小时间、失败率、吞吐量等非常实用的测试数据。

3. 其他细节问题

1)将测试结果保存到Excel表格

Excel表格具有非常强大的数据分析和统计汇总能力,将海量的测试数据导出到Excel表格是非常实用的,操作很简单,在线程组上点击右键,选择Add->Listner->View Results in Table,然后在Write results to file /Read from file中的Filename中填入一个指定的文件名即可,文件的扩展名最好是CSV,这样可以直接使用Excel打开。

2)全局分析

有多个线程组时,逐个分析比较麻烦,也不利于效率提升,只需要在TestPlan上点击右键,选择Add->Listner->Aggregate Report,即可对整个测试计划的所有线程组的测试执行情况一目了然,非常方便和实用,对于测试结果集,还可以点击Save Table Data将它保存到CSV文件中,然后直接用Excel打开,可以很快的自动转换成十分漂亮的图表。

4. 横向对比

1) 与Apache HTTP Server的ab工具对比

不同之处

ab工具对配置HTTP请求头状态、GET参数、POST提供数据和PUT提交文件,操作不够直观

ab工具对HTTPS的支持,需要单独编译的版本,默认的版本是不支持的,而ab工具并不能单独编译,因为它使用了Apache Portable Runtime(APR,可移植运行时库)中的源码,所以需要将整个Apache HTTP Server全部下载下来并且配置环境才能完成编译,使用门槛较高。

ab工具仅支持命令行模式,JMeter同时支持GUI模式和命令行模式,但是GUI模式需要消耗更多资源

ab工具效率和性能比JMeter高很多,所以对测试任务机要求低

ab工具的最大请求数不能超过50000

参数化、集合点、检查点这几大功能方面,JMeter要比ab工具强大的多

相同之处

  • 都可以对测试结果生成CSV文件
  • 都可以很方便的配置HTTP KeepAlive特性
  • 都提供了跨平台级的支持
  • 都对测试结果集的收集和分析做的很好

2) 与LoadRunner对比

不同之处

LoadRunner是一个非常庞大的工具,功能比JMeter要强大,操作也更加复杂,适合于专门长期从事性能测试领域的应用和研究

LoadRunner是商业软件,有价格不菲的授权许可费用,JMeter是开源软件

LoadRunner有比JMeter更加详细和庞大的文档系统,为完成复杂的测试任务提供了强有力的支持

LoadRunner由三大组件VuGen、Controllor、Analysis组成,JMeter是一个整体,没有做这样的拆分

LoadRunner的测试分析功能比JMeter要强大的多

LoadRunner对测试任务机的要求比JMeter要高出很多

LoadRunner不仅支持Java扩展,更支持强大C扩展,对测试脚本的可定制性比JMeter要复杂和强大

相同之处

  • 都支持在Windows和Linux平台上运行测试任务
  • 都具备设置集合点、检查点、参数化特性

5. 未来展望(下一个版本)

1)将会提供对MQTT的支持

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)并不是一门新技校,它是由IBM开发的一套面向M2M(Machine-to-Machine)的即时通信协议,基于TCP/IP的连接实现,所以它几乎支持所有平台,未来可能会成为物联网的一部分。

2)修复和改进

高分屏下的显示优化、文档改进、图片压缩、Label扫描优化。

6. 附加信息

1)JMeter的运行原理分析

JMeter模拟多用户并发,使用的JDK的线程模式,而且JDK的线程模式都是一堆native方法,它是JDK的C语言实现中根据不同的系统做出的处理,在Windows上最后会转为调用C标准库,在Linux上最后转为pthread库,所以当JMeter正在运行时,可以查看到它所依存的java进程的线程数量。

JMeter的代码量十分庞大,经过多年积累,本身的逻辑和体系结构也十分完善,它支持Web、HTTP、HTTPS、SOAP/REST、FTP、JDBC、LDAP、JMS、TCP、SMTP、POP3、IMAP,甚至支持Native模式的Shell脚本,GUI图表库使用的不是AWT,也不是SWT,而是Swing,界面显示优美,控件功能丰富。因此JMeter拥有一整套完整的类管理机制和接口。

JMeter还拥有很强的二次开发和扩展能力,因此甚至有团队用它来做回归测试而不是仅仅是性能和压力测试。

JMeter官方文档说的很清楚,JMeter不是一个浏览器,它不会解析HTML文档,也不会执行JS脚本。

JMeter还支持BeanShell。

JMeter对HTTP协议的Response,无论是JSON还是XML,都能正确的解析。

JMeter的一堆properties文件中,有数量庞大的定制参数,通过这些功能,可以实现更多可定制化的任务和需求。

JMeter还支持部署为集群服务器任务,执行超大规模的测试任务。

2)Apache ab工具的运行原理分析

ab工具使用C语言写成,但是它不是一个独立的项目,而是著名的httpd服务器项目的一个组件。它复用了APR的内存池技术,因此不会有频繁的内存申请和销毁动作,所以从基础设计上就保证了高并发测试任务的高效执行,同时又能有效避免内存泄漏或者溢出。

ab工具使用C语言写成,但是它不是一个独立的项目,而是著名的httpd服务器项目的一个组件。它复用了APR的内存池技术,因此不会有频繁的内存申请和销毁动作,所以从基础设计上就保证了高并发测试任务的高效执行,同时又能有效避免内存泄漏或者溢出。

Apache ab工具使用的是多路模式,依赖于创建非阻塞式的socket,以Poll的方式实现,pollset设计是一种优秀的设计方案,它不光是为了socket还可以是file。关键函数的原型是:

static void start_connect(struct connection * c);

关于这个函数,程序会依据并发数和执行次数对I/O实现多路复用,而且是用的多层循环遍历和递归的方式。通常情况下,软件开发时如果同时在一个函数中使用过多的循环嵌套和递归,会导致程序性能下降,甚至栈溢出,如果在这种情况下再大量使用全局变量,整个项目代码就会变的不可控,而ab却反其道而行之,在内存池的基础上,引入这种机制,实现了对非阻塞式socket的快速创建和遍历操作,然后再使用事件回调机制收集和处理结果,从而十分巧妙的同时实现了低内存、高并发、低CPU的目标,保证了极高的性能和效率。所以,网络上大量流传许多贴子说的ab工具是使用多线程模拟并发的结论是错误的,它是单进程单线程的工作模式,至于多线程,那已经是系统底层对异步socket的处理了,和ab工具是否是多线程并没有什么关系。程序运行之初,它调用下面的代码完成内存池的初始化:

apr_pool_create(&cntxt, NULL);

cntxt的类型是apr_pool_t,这是一个结构体,在所有测试完成之后,再次调用下面的代码完成内存池的释放:

apr_pool_destroy(cntxt);

它支持一堆复杂的命令行参数调用,然后根据这些参数配置选项,程序运行之初会在内存中创建几个十分重要的数据结构:

connection:结构体,HTTP请求实例数据结构,它包含与请求相关的关键数据,比如内存池管理实例ctx、Socket对象aprsock、Poll集合的描述对象实例pollfd以及请求本身相关的状态、时间、缓冲区大小、keep-alive等,具体的详细介绍请继续往下看。

apr_pool_t:结构体,这是内存池的管理实例,每一个HTTP请求都会产生这样一个实例,
apr_socket_t:结构体,包括自己内存池管理实例、socket类型、协议、本地地址、远程地址、端口号、是否阻塞方式、超时时间等调用选项。

apr_pollfd_t:结构体,pollset的描述,在ab工具中,会定义多个指向这个结构体数据结构的指针,在底层处理中,这些数据会生成一个链表。包括类型、请求事件标记、接收事件标记、Poll描述信息、当前调用者的上下文信息等。这个结构体中还有reqevents和rtnevents两个成员,是内核态和用户态轮询I/O状态和回调时用的事件句柄。

apr_size_t:也就是C语言中的size_t,无符号整型,保存当前HTTP请求在

apr_socket_recv上完成的大小信息。

apr_time_t:本质上是一个apr_int64_t,也就是long类型,保存请求时间相关的毫秒级的时间,包括启动时间、连接时间、请求写入时间、读取完成时间、请求完成时间,引用的是timestamp机制。

data:结构体,保存的是连接时间相关的所有数据,主要是启动时间、请求发出和读取响应之间的间隔时长(请求连接成功的时间)、整个连续所花费的时间、各连接之间的间隔时间。与这个结构体紧密绑定的还有几个compare函数,是用来处理测试结果的时候做数据比较用的。

apr_int64_t:对C语言中long类型的包装。

request:这是一个全局变量,指向大小为8192个char的数组的指针,里面保存的是HTTP请求的响应头信息,比如HTTP/1.0、Keep-Alive、Content-type等。

apr_pollset_t:pollset对象,pollset是一组socket的集合。

apr_sockaddr_t:HTTP请求的socket相关的数据结构。

简单的讲,ab工具中大量使用了APR中的数据结构,这些数据结构,是Apache根据需要对C语言的数据类型、网络、I/O、以及一些系统调用的再封装,完整的数据结构文档及其应用方法可以在这里看到,更加细致的网络相关的接口使用方法,还需要参考《Unix网络编程第1卷》,这本书的Chapter 6中作者详细介绍了五种I/O模型及其使用方法。

3)nmon工具的运行原理分析

nmon的原理比较简单,全部依赖于C语言标准库和Linux系统的内核文件系统/proc,没有第三方库的支持。程序运行之初,针对CPU、内存、硬盘、网络这些设备,分别定义了不同的函数,解析它们的内核参数和系统信息,然后创建一个主循环去轮询这些系统信息,并将结果输出到指定的设备中去,使用最多的库函数就是curses函数mvwprintw,获取到数据之后,以特定的方式组织起来保存到一个文本文件中去,对于这个保存的文件,使用nmonchart可以很轻松的将它转换成一个漂亮的HTML文档。

curses函数库是Linux下基于文本模式使用的图形函数库,它的出现,使得即使是文本模式下的Linux界面,也可以有非常漂亮的输出和显示,它可以很自由的定义输出的目标设备、颜色、形状等众多的特性。不同的Linux版本对curses的支持有所不同,所以当你下载下来nmon之后会发现它针对不同发行版的Linux有不同的运行程序,没关系,我们只需要保留我们想要使用的(比如CentOS),其他的一律删除即可,它们之间没有依赖关系,只需要一个主程序就可以运行了。执行服务器性能测试时nmon工具几乎是必不可少的,而nmon又有一堆参数需要配置,这种情况下我们可以把整个命令行和参数写到一个Shell脚本中,需要执行测试时只需要修改少量的几个关键参数,然后运行这个脚本即可,如果再自动调用nmonchart,然后将结果输出到HTTP服务器上,我们就可随时随地的动态的查看服务器的性能表现了。

4)结束语

这篇文档是我这两个星期执行性能测试以来的一些分析研究和总结,归纳整理出来,供以后需要的同学研究使用,这样可以免去大家百度和谷歌的时间,提升工作效率,同时也是我们本身工作的一种积累和归档,希望能为后来人提供一些参考和借鉴,鉴于本人学识有限,文中如有错误,敬请谅解。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

你可能感兴趣的:(自动化测试,软件测试工程师,软件测试,jmeter,职场和发展,功能测试,软件测试,自动化测试,程序人生)