我们在学习性能测试之前,需要有个新的认识:性能测试,不再是像功能测试一样单纯的找 Bug,而是去找性能指标
简单理解:一个接口返回的数据比较多(假设:不使用分页,把所有数据同时返回)
大数据测试的功能属于功能测试哦
在性能测试过程中发现一些问题,假设定位到某一段代码有问题,可以截图提交 Bug 给开发,但这并不是我们性能测试的最终目的,最终目的是找出性能指标
电商系统,下单业务,目前还不知道系统支持多少人同时下单,那么我们需要找到服务器能正常支持多少人同时下单
以下含义来源高老的解释,比较“官方”的术语
其实也算是一个简洁描述的性测试流程了
B/S架构:浏览器与服务器
C/S架构:客户端与服务器
性能测试与功能测试有所不同,性能测试更加关注系统的性能表现。而性能测试就是排除系统瓶颈,使系统更加健壮。可以从以下几个方面理解:
1)评估当前系统。系统未做过任何性能测试,对系统的当前性能情况不了解,缺少必要的的性能评估。
2)寻找瓶颈,优化性能。 常见的现象为,某业务操作响应时间很长、某系统上线一段时间后运行越来越慢,这些都需要分析定位并调优。
3)预测未来性能。当用户数和业务量增加时能否及时应对?如何调整?是增加应用服务器,还是数据库服务器?还是要优化代码逻辑?这一系列问题都值得思考,这也是性能测试目的所在。
响应时间越短,用户体验越好
单位时间内,网络处理的请求数量(事务/s)
网络没有瓶颈时,吞吐量≈TPS
TPS:每秒钟事务数。举例:例如每秒钟能完成200次注册操作,TPS的值越大越好
CPU、内存、磁盘、网络(磁盘速度最慢)
故事引入
有一个农夫决定买一匹骡子,他认为这个骡子至少 得能扛动3袋大米,他才会决定买这匹骡子(这相当于用户提出的性能需求)。结果他来到农贸集市上,试了好几头骡子,都不合适,最后终于有一头骡子能够比较轻松的扛动这3袋大米,而且还潇洒的走了几步(这相当于于性能测试通过)。 然后农夫高高兴兴地牵着这头骡子回家,而且给它扛了4袋大米(相当于让系统超负荷运行),因为他跑了太远才买到了这匹不可多得的骡子,他想看看它到底能有多强,所以农夫决定, 让这匹骡子就扛着这四袋大米走回家试试看,这匹骡子真的很厉害,刚开始的时候还一颠一跑的,可是后来实在路太远了,骡子越驮越费劲(在超负荷情况下检验系 统能正常运行多久,这相当于压力测试),快到家的时候,已经是走两步歇一步了。终于到家了, 农夫非常自豪地叫出自己的老婆,说:“老婆子,快来看看,看我买到了一头多么厉害的骡子啊!”,老婆出来后,农夫把他和骡子在一路上的经历都告诉了老太 婆,谁知这个老太婆却说:“你真蠢,这么大老远的路,也不让骡子驮着你,竟然和这头傻骡子一样走回来!”,农夫听了,觉得非常后悔,说:“那好吧,既然在 路上它没有驮我,那就让它现在补上,也算是对我的补偿。”,骡子还没有反应过来,就看那老农夫一个箭步,跳到了骡子背上(这相当于容量测试的极限点),可怜的骡子,无论如何也不会想到,这狠心的农夫竟然在它走了这么久之后,不但没有帮 它卸掉身上的重担,更没有给它喝口水,竟然变本加厉的跳到了它那本已弯曲的背上。可怜的骡子啊,就这么一命呜乎了!就看见那个骡子、农夫和4袋麦子一起轰然倒地。(相当于已经到了系统的最大拐点,造成了系统瘫痪,无法使用,容量测试结束)。
性能测试(Performance Test):通常收集所有和测试有关的所有性能,通常被不同人在不同场合下进行使用。测试软件在系统中的运行性能,度量系统与预定义目标的差距。
关注点:how much和how fast
负载测试(Load Test):负载测试是一种性能测试,指数据在超负荷环境中运行,程序是否能够承担。通过逐步增加系统负载,确定在满足性能指标的情况下,系统所能承受的最大负载量。
关注点:how much
压力测试(Stress Test):压力测试是一种高负载下的负载测试,也就是说被系统处于一个负载的情况,再继续对他进行加压,形成双重负载,知道系统崩溃,并关注崩溃后系统的恢复能力,以前再加压的一个过程,看看系统到底是否已经被彻底破坏掉了。
有个很形象的说法就是:你能够承担100千克的重量,而且也能走,但是你能否承担100千克的重量行走1个月。
我觉得有一句话描述的很好:外部的负载叫压力,内部的压力叫负载。负载注重关注内部的以及系统自身一些情况;而压力更关注系统外部的表象.
通过逐步增加系统负载,确定在满足性能指标的情况下,系统所能承受的最大负载量。
为了寻找最佳用户数,最大用户数
最佳用户数:只要CPU、内存、硬盘某一项达到80%那么就是最佳用户数
最大用户数:系统崩溃之前的的用户数。比如1000个用户CPU达到100%,这时系统还能正常运行,但是一旦多家1个用户这个时候系统就会崩溃,那么1000就是最大用户数。
通过增加“用户数”,就是常说的并发数
天平秤,称东西的时候,需要逐步加砝码,最终达到砝码和物品重量的平衡点,因为它不可能一下子就达到平衡点【好比不可能一下子找到系统能承受的最大负载量】
往往是在负载测试的基础上进行的,是一种高负载下的负载测试(过载)。说白了就是系统已经处于负载下,再对它进行加压,形成双重负载,直到系统崩溃。
向服务器发送大量的请求,并且长时间运行,用于测试服务器在高负载情况下的稳定。
高负载:保持最大用户数
压力测试就是求当前系统的极限值
问: 大家什么时候会觉得工作压力大?
答: 996、007;因为你不会觉得955压力山大吧
结论: 所以在我们日常工作中,长时间工作强度高,才会觉得压力大;如果你一周就加班一天也说压力大…(那就别干这一行了)
测试系统的稳定性
啥情况算不稳定?稳定性差?
隔三差五的出现下面的情况
怎么分析是服务异常还是服务器异常
总结
压力测试长时间运行,可能会逐渐增加系统的内存占用空间,若得不到有效的内存回收,当达到内存最大值时,系统就会崩掉
电商秒杀场景,几十个商品几十万个人同时秒杀抢购
肯定有
场景类比
从一袋米中找一个绿豆,和一碗米中找一个绿豆,找的时间肯定是千差万别的
基于负载测试的性能指标,通过长时间运行。一般是48-72小时。
有些企业把稳定性测试也叫压力测试。
基准测试-----负载测试-----压力测试
响应时间
从发起请求到收到请求响应的时间整个过程所耗费的时间。越快越好,响应时间越短,用户体验越好
等价于: 发起请求网络传输时间 + 服务器处理时间 + 数据库系统处理时间 + 返回响应网络传输时间
操作浏览器发送请求:
点击前端页面-->后端接口-->通过网络发送给服务器-->在数据库里面查询获取需要的内容-->返回给后端接口-->后端接口再给前端-->前端页面负责展示
这些一系列过程加起来的时间才是响应时间,网络情况也会影响响应时间
平均响应时间
指系统稳定运行时间段内,同一交易的平均响应时间。一般而言,交易响应时间指的就是平均响应时间。
不同行业不同业务可接受的响应时间是不同的,一般情况,对于在线实时交易:
对于批量交易:
HPS:每秒钟点击数。
TPS:每秒钟事务数。每秒钟处理的业务越多越好。例如每秒钟能完成200次注册操作,TPS的值越大越好
QPS:每秒钟查询数。
对于互联网业务中,如果某些业务有且仅有一个请求连接,那么TPS=QPS=HPS,一般情况下用TPS来衡量整个业务流程,用QPS来衡量接口查询次数,用HPS来表示对服务器单击请求。
每秒钟事务数。每秒钟处理的业务越多越好。例如每秒钟能完成200次注册操作,TPS的值越大越好
衡量服务器处理能力的最主要指标
如果一个用户点击了一次,发出来 3 个 HTTP Request,调用了 2 次订单服务,调用了 2 次库存服务,调用了 1 次积分服务
**问:**Request 数量如何计算
**答:**3+2+2+1 = 8?不, 应该是 3,因为发出了 3 个 Request,而调用服务会有单独的描述,以便做性能统计
上图的订单服务、库存服务、积分服务,各调用了2、2、1次,还是比较好理解的
无论TPS、QPS、HPS,此指标是衡量系统处理能力非常重要的指标,越大越好,根据经验,一般情况下:
单位时间内,在网络传输的数据量的平均速率(kB/s)
定义及解释
中央处理器是一块超大规模的集成电路,是一台计算机的运算核心(Core)和控制核心( Control Unit)。它的功能主要是解释计算机指令以及处理计算机软件中的数据。 CPU Load: 系统正在干活的多少的度量,队列长度。系统平均负载。
简称
Central Processing Unit:CPU
标准
CPU指标主要指的CPU使用率利用率,包括用户态(user)、系统态(sys)、等待态(wait)、空闲态(idle)。CPU 利用率要低于业界警戒值范围之内,即小于或者等于75%;CPU sys%小于或者等于30%, CPU wait%小于或者等于5%。单核CPU也需遵循上述指标要求。CPU Load要小于CPU 核数。
定义及解释
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。内存使用率不能超过80%
简称
Memory就是内存的简称。
标准
现代的操作系统为了最大利用内存,在内存中存放了缓存,因此内存利用率100%并不代表内存有瓶颈,衡量系统内有瓶颈主要靠SWAP(与虚拟内存交换)交换空间利用率,一般情况下,SWAP交换空间利用率要低于70%,太多的交换将会引起系统性能低下。
SWAP
内存中swap交换区间的使用完成意味着,物理内存耗尽,一般要避免这种情况,物理内存使用不要超过80%
SWAP分区:我们在安装系统的时候已经建立了 swap 分区。swap 分区通常被称为交换分区,这是一块特殊的硬盘空间,即当实际内存不够用的时候,操作系统会从内存中取出一部分暂时不用的数据,放在交换分区中,从而为当前运行的程序腾出足够的内存空间。
也就是说,当内存不够用时,我们使用 swap 分区来临时顶替。这种“拆东墙,补西墙”的方式应用于几乎所有的操作系统中。
计算机用户会经常遇这种现象。例如,在使用Windows系统时,可以同时运行多个程序,当你切换到一个很长时间没有理会的程序时,会听到硬盘“哒哒”直响。这是因为这个程序的内存被那些频繁运行的程序给“偷走”了,放到了Swap区中。因此,一旦此程序被放置到前端,它就会从Swap区取回自己的数据,将其放进内存,然后接着运行。
定义及解释
磁盘吞吐量是指在无磁盘故障的情况下单位时间内通过磁盘的数据量。
简称
Disk Throughput。
标准
磁盘指标主要有每秒读写多少兆,磁盘繁忙率,磁盘队列数,平均服务时间,平均等待时间,空间利用率。其中磁盘繁忙率是直接反映磁盘是否有瓶颈的重要依据,一般情况下,磁盘繁忙率要低于70%。不要频繁读取磁盘
定义及解释
网络吞吐量是指在无网络故障的情况下单位时间内通过的网络的数据数量。单位为Byte/s。网络吞吐量指标用于衡量系统对于网络设备或链路传输能力的需求。当网络吞吐量指标接近网络设备或链路最大传输能力时,则需要考虑升级网络设备。
简称
Network Throughput
标准
网络吞吐量指标主要有每秒有多少兆流量进出,一般情况下不能超过设备或链路最大传输能力的70%。性能测试中最好不要测上传和下载功能
指同一个时间点执行相同的操作(如:秒杀)
高速公路上,同时有多少辆车经过同一个关卡,但不一定是同一个牌子的汽车
假设有 10 个用户数,每个用户同一时间点内发起 2 个请求,那么服务器收到的请求并发数就是 20
Badboy是里面内置了一款IE浏览器,通过人为操作内置的这个浏览器,工具会帮助我们自动生成脚本
Badboy内置的IE浏览器不兼容,如果项目兼容了IE那么可以用此工具
下载地址
http://www.badboy.com.au/
教程地址
https://www.cnblogs.com/auguse/articles/13881806.html
jmeter自带的非测试元件中HTTP代理服务器录制
jmeter会启动一个监听端口,默认是8888
测试计划----->添加----->非测试元件----->HTTP代理服务器
jmeter会启动一个监听端口默认8888,可以修改,建议修改10000以上端口号
Edge、Firefox、Chrome使用插件SwitchyOmega创建一个情景模式
测试计划----->添加----->线程----->线程组
添加录制控制器:线程组----->逻辑控制器------->录制控制器
如果报下面异常,需要更改端口号,SwitchyOmega也需要更改。建议10000以上
参数解释
名称:可以给线程组设置一个个性化的命名
注释:可以对线程组添加备注以标记
在取样器错误后要执行的动作:就是在错误之后要如何执行,可选继续执行后续的、停止执行等。
线程数:就是需要设置多少线程执行测试。
Ramp-up Period (in Seconds):用于告知JMeter 要在多长时间内建立全部的线程。多长时间内需要把上面的线程数均匀启动完。 默认值是0。如果未指定ramp-up period ,也就是说ramp-up period 为零, JMeter 将立即建立所有线程。假设ramp-up period 设置成T 秒, 全部线程数设置成N个, JMeter 将每隔T/N秒建立一个线程。
循环次数:就是决定一个线程要跑多少次测试。
Delay Thread creation until needed:直到需要时延迟线程的创建
调度器:选中之后可以配置启动时间,立即或者预定的时间名称:可以给线程组设置一个个性化的命名
注释:可以对线程组添加备注以标记
在取样器错误后要执行的动作:就是在错误之后要如何执行,可选继续执行后续的、停止执行等。
线程数:就是需要设置多少线程执行测试。
Ramp-up Period (in Seconds):用于告知JMeter 要在多长时间内建立全部的线程。多长时间内需要把上面的线程数均匀启动完。 默认值是0。如果未指定ramp-up period ,也就是说ramp-up period 为零, JMeter 将立即建立所有线程。假设ramp-up period 设置成T 秒, 全部线程数设置成N个, JMeter 将每隔T/N秒建立一个线程。
循环次数:就是决定一个线程要跑多少次测试。
Delay Thread creation until needed:直到需要时延迟线程的创建
调度器:选中之后可以配置启动时间,立即或者预定的时间
线程组----->添加----->配置元件----->HTTP请求默认值
教程:https://www.cnblogs.com/auguse/articles/13908104.html
创建正则表达式提取器
提取sessionid
1.引用名称
自己定义的变量名称,后续请求将要引用到的变量名,如填写的是:user_id,后面的引用方式是${user_id}
2.正则表达式
提取内容的正则表达式,相当于lr中的关联函数
3.模板
如果一条正则表达式有多个提取结果,则提取结果是数组形式
模板 $1$、$2$.....表示把解析到的第几个值赋给变量,从 1 开始匹配
$0$ 表示整个表达式匹配的内容(后续具体看栗子)
若只有一个结果,只能是$1$
4.匹配数字
0:随机,默认
-1:所有的值
1:第一个值。
如果使用-1的话,还可以通过${user_id_1}的方式来取第1个匹配的内容,${user_id_2}来取第2个匹配的内容。
5.缺省值
缺省值,匹配不到值的时候取该值
将正则表达式的结果存储在变量名sid
创建cookie管理器,添加sessionid
后续请求携带,使用HTTP cookie管理器实现,注意使用变量的值的形式: ${sid}
教程:https://www.cnblogs.com/auguse/articles/13904546.html
业务中脚本中有登录操作,需要输入用户名和密码,假如系统不允许相同的用户名和密码同时登录,或者想更好的模拟多个用户来登录系统。这个时候就需要对用户名和密码进行参数化,使每个虚拟用户都使用不同的用户名和密码进行访问。
测试数据:不能有表头,比如username,password
txt编码格式需要另存为ANSI格式。
创建函数助手
点击生成后,会自动复制,在参数化时直接复制即可
CSV file to get values from | *alias:这里需要填入txt或者csv文件的绝对路径,我这里用txt作为保存数据
CSV文件列号| next| *alias:表示当前变量读取第几列数据,第一列是0。
填完之后,就点击生成。这里就会生成函数字符串了,先复制这段字符串
参数化
将我们写好的函数复制到“登录”页面用户名和密码的位置。
如果txt不转换编码生成后下面不出现内容,理应出现第1列内容
可以使用${__Random}随机读取数据
教程:https://www.cnblogs.com/auguse/articles/13908271.html
思考时间(Think Time)也称为“休眠时间”,是指用户在进行操作时,每个请求之间的时间间隔。对于交互系统来说,用户不可能持续不断地发出请求,一般情况下,用户在向服务端发送一个请求后,会等待一段时间再发送下一个请求。性能测试过程中,为了模拟这个过程而引入思考时间的概念。在测试脚本中,思考时间为脚本中两条请求语句之间的间隔时间。当前对于不同的性能测试工具提供了不同的函数来实现思考时间。
调试脚本别用,影响效率,真实场景下可以用
1秒=1000毫秒
1.创建定时器
需要让每个线程在请求之前按相同的指定时间停顿,就可以使用这个定时器;需要注意的是,固定定时器的延时不会计入单个sampler的响应时间,但会计入事务控制器的时间
2.设置间隔时间
3.定时器作用域
1.创建定时器
该计时器将每个线程请求暂停一个随机的时间量,每个时间间隔的发生概率相同。总的延时 = 随机延时 + 偏移延时值。
Random Delay Maximum(in milliseconds):随机延迟最大值(以毫秒为单位)
Constant Delay Offset(in milliseconds):恒定延迟偏移量(以毫秒为单位)
2.设置间隔时间
每个步骤之间的时间间隔 = 随机值+恒定值
教程:https://www.cnblogs.com/auguse/articles/11684181.html
模式匹配规则
选项有: 包括、 匹配、 Equals、 Substring 、否。
a.包括:返回结果包括你指定的内容,支持正则匹配相当于 equals 。当返回值固定时,可以返回值做断言,效果和equals相同
b.匹配 :
● 相当于 equals 。当返回值固定时,可以返回值做断言,效果和equals相同
●正则匹配 。 用正则表达式匹配返回结果,但必须全部匹配。 即正则表达式必须能匹配整个返回值,而不是返回值的一部分。
c.Equals : 返回结果与你指定断言完全一致
d.SubString:与 “包括”差不多,都是指返回结果包括你指定的内容,但是subString不支持正则字符串
e.否:就相当于取反。 如果上面断言结果为true,勾选“否”后,最终断言结果为false。如果上面断言结果为false,勾选“否”后,则最终断言结果为 true。
1.创建响应断言
2.填写断言内容,一般选择文本断言
教程:https://www.cnblogs.com/auguse/articles/11684066.html
1.创建同步定时器
2.设置集合点
Number of Simulated Users to Group by
虚拟用户组的数量:一定要小于或者等于当前脚本线程组中的线程数
Timeout in milliseconds
超时时间:如果设置为0,达到了Number of Simulated Users to Group by设置的值才会释放。
如果大于0,那么超过等待时间后还没到达Number of Simulated Users to Group by设置的值,Timer(定时器)将不再等待,会释放已到达的线程
如果设置为0,且线程组的数量无法达到Number of Simulated Users to Group by设置的值,那么将无限等待,除非手动停止
Synchronizing timer 仅作用于同一个JVM中的线程,所以,如果使用并发测试,确保"Number of Simultaneous Users to Group by"中设置的值不大于它所在线程组包含的用户数。
Synchronizing Timer是在每个sampler(采样器)之前执行的,而不是之后,不管这个定时器的位置放在sampler之后,还是之前。
作用域:当执行一个sampler之前时,和sampler处于相同作用域的定时器都会被执行。
如果希望定时器仅应用于其中一个sampler,则把该定时器作为子节点加入,如下图:Synchronizing Timer 所属于 HTTP请求。
教程:https://www.cnblogs.com/auguse/articles/13908340.html
调式代码的时候用,真实场景要禁用,因为大量请求时,启用该监听器时打印的日志比较多,会造成大IO消耗,影响压力机性能
显示请求和响应的细节。比如取样器结果、请求体、请求头、响应体、响应头
察看结果树放在线程组下面则会查看所有请求;放在某个请求下面,只查看当前请求;放在某个控制器,则查看此控制器下节点执行的结果
注意:在没有对请求断言的情况下,显示绿色并不一定是成功,只代表响应码是200或300系列,显示红色说明响应码是400或500系列。所以要想确定请求返回的是正确的,必须要加上断言,只有断言成功才会显示绿色。
汇总报告,为测试中的每个不同命名的请求创建一个表行。这与聚合报告类似,只是它使用更少的内存。提供了最简要的测试结果信息,同时可以配置 将相应的信息保存至指定的文件中(支持xml、csv格式的文件)。单击Configure按钮,可以配置结果保存各种选项
参数说明
Name:名称,可以随意设置,甚至为空;
Comments:注释,可随意设置,可以为空;
Label 取样器别名,如果勾选Include group name ,则会添加线程组的名称作为前缀
Samples 取样器运行次数
Average 请求(事务)的平均响应时间
Min 请求的最小响应时间
Max 请求的最大响应时间
Std. Dev 响应时间的标准方差
Error % 事务错误率
Throughput 吞吐量 也就是TPS
Received KB/sec 每秒收到的千字节
Sent KB/sec 每秒发送的千字节
Avg. Bytes 响应平均流量
Name:名称,可以随意设置,甚至为空;
Comments:注释,可随意设置,可以为空;
Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,这里显示的就是 Name 属性的值
#Samples :表示测试中一共发出了多少个请求,如果模拟10个用户,每个用户迭代10次,那么这里就显示对应的 HTTP Request的执行次数是100
Average :平均响应时间——默认情况下是单个 Request 的平均响应时间,当使用了 Transaction Controller 时,也可以以Transaction 为单位显示平均响应时间
Median :50%用户的响应时间
90%Line :90%用户的响应时间
95%Line :95%用户的响应时间
99%Line :99%用户的响应时间
Min :最少响应时间
Max :最大响应时间
Error% :本次运行测试中出现错误的请求的数量/请求的总数
Throughput :吞吐量,默认情况下表示每秒完成的请求数(Request per Second),当使用了 Transaction Controller 时,也可以表示类似 LoadRunner 的 Transaction per Second 数
Received KB/src:每秒从服务器端“接收”到的数据量
Sent KB/src :每秒从服务器端“发送”到的数据量
教程:https://www.cnblogs.com/auguse/articles/13908379.html
创建请求默认值
填写协议、ip地址、端口号。起到全局作用。这样别的请求不用在继续输入协议+ip+端口了
性能测试中,为了模拟人的操作,所以我们要做打开登录首页的请求
后续请求携带,使用HTTP cookie管理器实现,注意使用变量的值的形式: ${sid}
woniuboss系统中手动查询员工信息。
不输入值,直接点击查询按钮,即可抓包所有员工信息。想看一个人的,输入人名即可
通过正则表达式提取当前所有员工名字和员工id。新增采购登记需要员工名和员工id
purchase_employee:填写通过正则表达式随机出来的员工名
ass.purchase_employee_id:填写通过正则表达式随机出来的员工id
察看结果树执行后会发现新增失败,通过调试取样器发现,随机出来的员工名和id的变量名不是正则表达式提取的变量名yid,而是最下面的yid_g1和yid_g2,那么就需要在新增采购登记请求中修改变量名了
修改新增采购登记的变量名
新增成功
教程:https://www.cnblogs.com/auguse/articles/13909758.html
1.安装插件
Servers Performance Monitoring
2.将 ServerAgent-2.2.3.zip上传到linux服务器中,放在tmp目录下解压
a.ServerAgent-2.2.3要可以启动,必须要有java环境,其实就是安装jdk
https://www.cnblogs.com/auguse/p/13325310.html
b.要在防火墙里面开放端口 4444
https://www.cnblogs.com/auguse/p/13325522.html
c.启动
3.创建 PerfMon Metrics Collector
4.参数填写
事务(Transaction)是指用户在客户端做一种或多种业务所需要的操作集,通过事务函数可以标记完成该业务所需要的操作内容;另一方面可以用来统计用户操作的相应时间。事务响应时间是指通过记录用户请求的开始时间和服务器返回内容到客户时间的差值来计算用户操作响应时间的。
事务是一系列操作步骤的集合,操作步骤是属于事务里面的,就是说以后我们在线程组下面先要添加事务,在事务中添加请求。
线程组----->事务控制器----->请求
1.创建事务控制器
2.所有请求放在事务控制器下
教程:https://www.cnblogs.com/auguse/articles/13909953.html
1.命令生成
jmeter.bat路径+woniuboss4.0.jmx路径+报告存储路径+log
E:\testtool\apache-jmeter-5.3\bin\jmeter.bat -n -t E:\codejmeter\woniuboss4.0.jmx -e -o E:\codejmeter\report -l denglu.log
-n:以非GUI形式运行Jmeter
-t:脚本文件(.jmx)的路径
-l:记录样本到文本,可以看成日志,文件名为.log即可
-e:在脚本运行结束后生成html报告
-o:用于存放html报告的路径
report是手动创建的测试报告文件夹,每次启动命令之前,文件夹内容必须清空,否则报错
2.执行完毕后,用浏览器打开生成的文件目录下的index文件
教程:https://www.cnblogs.com/auguse/articles/11684117.html
打开JMeter上的plugin manager,点击AvailablePlugns下拉滚动条到底部,勾选Custom Thread Group,点击右下角的Apply Changes and Restart JMeter,安装好了会出现在Installed Plugins列表里。
执行线程释放过程中,执行的线程依然在运行
并发用户数直接上升到最大用户数,持续一段时间后全部停止,一般用于压力测试或可靠性测试
并发用户数逐渐上升,持续一段后慢慢下降,从而确定当前配置的系统环境能够支撑的最大/最佳用户数
模拟还真是服务器访问情况,需要大量历史数据作为支撑
综合利用上述场景
注册用户数一般指的是数据库中存在的用户数。
一般情况下,注册用户数,不一定会对服务器产生压力,在选择线程数的时候,一般老板或者客户告诉你的都是注册用户数,线程取其中到5%-20%之间即可,一般选择5%。
在性能测试中1个线程不是指1个用户,代表100左右个用户。
在线用户数只是 ”挂” 在系统上,对服务器不产生压力,注册用户数一般指的是数据库中存在的用户数。**
2000个线程怎么分配?
需要统计页面的访问量(pv),根据访问的数量比例计算
不能使用root用户,需要新建用户
#新建用户
useradd -g root chen
#设置密码123456
passwd chen
Jmeter本身是需要产生压力也就是线程的,如果使用它监控,产生的线程就会减少
top:实时监控系统资源
vimstat 2:每隔2秒监控系统资源。数字可改变
free:查看当前系统的内存情况,包括swap空间
top详解
https://www.cnblogs.com/auguse/articles/13934391.html
vimstat详解
https://www.linuxprobe.com/linux-vmstat-do.html
监控容器的
适合使用docker容器部署的项目
适合监控传统方式部署的项目
教程:https://www.cnblogs.com/auguse/category/1563005.html
不建议下载最新版
以读取MySQL数据库为例,下载一个mysql驱动包,mysql官网下载网址:https://downloads.mysql.com/archives/c-j/
Select Operating Systems:选择Platform independent
然后选择zip包,点击Download。对其进行解压,找到mysql-connector-java-8.0.22.jar文件
打开jmeter,新建一个测试计划,选中测试计划,点击浏览,选择上文找到的mysql-connector-java-8.0.22.jar,点击Open就好。
建一个线程组,右击线程组,添加-配置元件-JDBC Connection Configuration
新建线程组
新建JDBC Connection Configuration
参数说明
Variable Name for created pool:填写一个连接名称,自定义名称(如mysqltest),在后面的JDBC请求中需要用到,以适配连接的是该连接名称的数据库配置。
Database URL:jdbc:mysql://数据库ip:端口号/数据库名 +?serverTimezone=UTC这个是因为,后面JDBC请求不成功,说是时区的问题,所以便加上这个。
如:jdbc:mysql://192.168.0.112:3307/test?serverTimezone=UTC
JDBC Driver class:com.mysql.jdbc.Driver(不同的数据库不一样,这个是mysql的)
Username:数据库登录名
Password:数据库登录密码
右击线程组,添加-取样器-JDBCRequest
新建JDBCRequest
参数说明
Variable Name of Pool declared:对应上文Variable Name for created pool的设置值,如mysqltest
Variable Names:对应sql查询结果的字段值, 表字段值有多少个,则这里对应值就有多少个。如下文查询出username,password,address,emai字段,设置变量名则为username,password,address,emai
右击线程组,添加-监听器-察看结果树 ,然后执行脚本,可以看到Text中JDBC Request请求为绿色的,说明请求成功。响应数据为查询数据库的数据。跟数据库查询结果一致。
新建查看结果树
查询结果
Jmeter中通过${}形式来取参数值,当取值为变量,${变量名},如上文中引用username的字段值,${username}
a.性能需求分析
挑选用户使用最频繁的功能来做测试,比如:登陆,搜索,提交订单
确定性能指标,比如:事务通过率为100%;90%的事务响应时间不超过5秒;并发用户为1000人时CPU和内存的使用率在70%以下
b.性能测试计划
确测试时间和测试环境和测试工具的选择
注明测试通过指标以及业务场景
准备性能测试数据
c.搭建性能测试环境
注意这里测试环境一定要和线上正式环境保持一致
d.通过性能测试用例,编写性能测试脚本
性能测试脚本进行调优,设置检查点、参数化、关联、集合点、事务,调整思考时间
e.设计性能测试场景,监控服务器,运行测试场景
f.分析性能测试结果,判断性能瓶颈,反馈结果信息
g.回归性能测试
h.编写性能测试报告
老板或者客户说:我们网站会有百万级访问量(百万级不等于线程数量)
实际:
在性能测试中1个线程不等于1个人,这两个不能划等,1个线程可以大约看成100人左右
老板和客户所说额其实是最佳用户数,在测试指标一定的情况,服务器所能承受的访问量:
性能测试目标:
CPU使用率不能超过75%
内存使用率不能超过80%
磁盘的繁忙率不能超过70%
网路的使用率不能超过70%
满足20000个并发用户的访问
测试的目标、范围、时间
测试资源:人力、物力(硬件环境、网络环境、软件环境需求)
通过标准
挂起恢复条件
业务场景(业务有哪些)
测试数据准备
根据业务场景来
教程:https://www.cnblogs.com/auguse/articles/13928733.html
Jmeter版本、插件、测试数据必须一致
连接对方,对方必须开放端口
.主机应该把Jmeter全部打包给从机,统一起来
fiddler抓包响应结果
jmeter响应结果是英文,如何解决?
解决办法
将Accept-Language和User-Agent添加至HTTP信息头管理器
结果展示
为什么登录抓包显示POST请求,而在jmeter中请求又变成GET请求?
因为开发给接口做了重定向,而Jmeter默认选择了跟随重定向
为什么登录请求在Fiddler响应式空的,而在Jmeter显示html文本,反而像是登录后的显示信息?
因为做了302重定向,而Jmeter默认选择了跟随重定向
选择自动重定向后,虽然变成了POST请求,但是登录的响应显示不是登录后页面,反而显示未登录状态
正则表达式提取sessionid,调试取样器为什么没有提取出来?
因为登录接口做了重定向,而Jmeter默认选择了跟随重定向,这时需要取消掉跟随重定向和自动重定向
这时响应头也有了sessionid
登录后显示的页面
与往常做的请求不一样,这次的请求类型是json类型
将消息体数据拷贝进下面位置。
此项目中prop_cm_name和prop_cm_content不能重复,需要改名
创建文件失败,且发现自动携带了token,我们并没有提取token,为什么就自动携带了呢?是因为之前登录重定向的原因,但是这里请求自动携带的token是没有效果的。请求还是会失败。
所以我们要在登录后的请求时去提取token,需要再添加一个正则表达式。这是需要注意的是这是正则表达式提取不是在网页的主体去提取,而是在次体提取。
在Fiddler找重定向的请求,提取token
创建正则表达式提取token,在次体中提取,同时要将跟随重定向选中
选中跟随重定向,运行脚本,调试取样器就可以看见提取的token了
可是sessionid又去哪了?需要注意的是用户仪表板请求已经携带了sessionid,所以在这个项目中不携带sessionid是可以的
请求头中携带token,和下面的token有何区别?
请看下图,上面的token是经过url解码的,而下面中的token是经过url编码的。下面token解码完成后就是上面的token,所以在这个请求前我们应该将url先进行解码。那么如何解码呢?使用Bran Shell
package com.demo;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;//解码
import java.net.URLEncoder;//编码
//bianma指的是类名
public class bianma {
public static void main(String[] args) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
//将蜗牛学院 进行编码
String str1 = "蜗牛学院";
String str2 = URLEncoder.encode(str1, "UTF-8");
//输出编码后的内容
System.out.print("这是编码后的:"+str2+"\n");
//解码前的内容
String str3 = "%E8%9C%97%E7%89%9B%E5%AD%A6%E9%99%A2";
//对于编码的结果在进行解码
String str4 = URLDecoder.decode(str3, "UTF-8");
System.out.print("这是解码后的:"+str4);
}
}
在用户仪表板下进行解码。因为是在登录请求之后,用户仪表板之前进行url编码的,所以在用户仪表板下进行解码
创建BeanShell Listener
java代码对url进行解码
cookie管理器引用
调试取样器查看解码后的token
此请求头中有一个url解码的token,所有应该在此请求下单独创建一个HTTP信息管理器,引用解码变量
为什么还会报错?
因为Content-Type是application/json,所以应该此请求的HTTP信息头管理器添加json信息头
不能添加到最上面的HTTP信息头管理器,否则所有请求都是json类型
登录一定要勾上自动重定向或者跟随重定向,一般选择跟随重定向。不勾获取不到token
要在次体中使用正则表达式提取token
以管理员身份(QA)运行
Virtual User Generator:类似于一个IDE工具,用来进行脚本开发,录制脚本,调试脚本
Controller:设置执行场景,产生线程,进行脚本执行。运行场景,压力测试
Analysis:对于Controller执行的结果进行分析,可以生成可视化测试报告
教程:https://www.cnblogs.com/auguse/articles/13965326.html
将LoaderRunner12版本的wplus_init_wsock.exe替换掉11版本bin目录下的。LoaderRunner要处于关闭状态
新建 New Virtual User
选择Web(HTTP/HTML),点击Create新建
设置录制选项
选择bin目录下的wplus_init_wsock.exe,然后点击Options
Recording选项:对于web应用,一般选择HTML_BASED SCRIPT模式下的A script ccntaining explicit URLs only进行录制脚本,避免脚本之间的前后依赖关系。
选择UTF-8
取消自动关联
使用代理录制,进行以下参数设置,弹出新增代理服务器设置页面,上面输入系统服务器的地址(IP或者域名不用写http://)和端口号,Service Id 选择http,下面的监听端口号输入浏览器或者手机代理上设置的端口号(端口号不要被占用即可)随后点击Update
浏览器SwitchyOmega代理插件,添加代理。Edge、Chrome、Firefox都可以添加这个插件
点击ok后会卡顿一会,因为11版本用的是12版本的录制插件
处于录制状态,录制时不能关闭弹窗,也不能点击Shutdown按钮,否则无法录制
登录WoniuBoss4.0,做登录和注销操作,然后点击停止录制
停止录制后,会保存,回到LoadrRunner就可以看到录制的脚本了
保存
执行录制的脚本
查看结果
为什么录制脚本时取消了关联,后续的请求还能成功?
因为在录制的过程中,已经把cookie录制进去了,只要cookie没过期那么录制的脚本就能执行成功。有的网站的cookie长期都不会过期,比如WoniuBoss4.0就是这种情况,这样很不安全。
创建函数web_url、web_submit_data
搜索函数
函数及说明
1.web_url: 用于进入或者打开一个网页的时候使用,其实发出一个get请求
url:请求url地址
Resource:0代表非资源,1代表资源
Referer: 后面跟的是当前请求的上一个url地址。由于这个请求时第一个所以后面没有
2.web_submit_data
Method:请求的类型,只支持post 和 get
Action:请求的url地址
ITEMDATA:里面写的请求的参数
代码
Action()
{
//打开首页
web_url("打开首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/login",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//登录
web_submit_data("登录",
"Action=http://172.16.6.237:8888/WoniuBoss4.0/login/userLogin",
"Method=POST",
"TargetFrame=",
"Referer=",
ITEMDATA,
"Name=userName", "Value=WNCD000", ENDITEM,
"Name=userPass", "Value=woniu123", ENDITEM,
"Name=checkcode", "Value=0000", ENDITEM,
"Name=remember", "Value=Y", ENDITEM,
LAST);
//登录后首页
web_url("打开首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/main",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
return 0;
}
创建函数
1.创建注册型关联函数
web_reg_save_param_ex(
"ParamName=sid",
"LB=JSESSIONID=",
"RB=;",
SEARCH_FILTERS,
"Scope=HEADERS",
LAST);
2.代码调试
-sessionid一定要放在登录函数前面,而想通过调试打印出sessionid需要放在登录函数后面
lr_error_message(lr_eval_string("{sid}"));
3.将提取出来的sessonid添加到cookie中,跨域DOMAIN后面跟服务器的ip,如果不添加的话脚本可能不执行
web_add_cookie("JSESSIONID={sid}; DOMAIN=172.16.6.237");
代码
Action()
{
//打开首页
web_url("打开首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/login",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//注册型函数放在目标函数的前面
// 提取sessionid,赋给变量sid
web_reg_save_param_ex(
"ParamName=sid",
"LB=JSESSIONID=",
"RB=;",
SEARCH_FILTERS,
"Scope=HEADERS",
LAST);
//登录
web_submit_data("登录",
"Action=http://172.16.6.237:8888/WoniuBoss4.0/login/userLogin",
"Method=POST",
"TargetFrame=",
"Referer=",
ITEMDATA,
"Name=userName", "Value=WNCD000", ENDITEM,
"Name=userPass", "Value=woniu123", ENDITEM,
"Name=checkcode", "Value=0000", ENDITEM,
"Name=remember", "Value=Y", ENDITEM,
LAST);
//代码调试,打印出sessionid
lr_error_message(lr_eval_string("{sid}"));
//将提取出来的sessonid添加到cookie中,跨域DOMAIN后面跟服务器的ip,如果不添加的话脚本可能不执行
web_add_cookie("JSESSIONID={sid}; DOMAIN=172.16.6.237");
//登录后首页
web_url("登录后首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/main",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//点击公告发布
web_url("点击公告发布",
"URL= http://172.16.6.237:8888/WoniuBoss4.0/notice/add",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//发布公告
web_submit_data("doAdd",
"Action=http://172.16.6.237:8888/WoniuBoss4.0/notice/doAdd",
"Method=POST",
"TargetFrame=",
"RecContentType=text/plain",
"Referer=http://172.16.6.237:8888/WoniuBoss4.0/notice/add",
"Snapshot=t10.inf",
"Mode=HTML",
ITEMDATA,
"Name=title", "Value=哈哈", ENDITEM,
"Name=content", "Value=\如上图所示,这个界面是安装与 JDK 同版本的 JRE张三\n\n\n张三\n\n\n张三\n\n\n张三\n\n
", ENDITEM,
LAST);
return 0;
}
教程:https://www.cnblogs.com/auguse/articles/13972893.html
编辑数据
数据存放在txt文档中,参数化文件如果出现乱码,请另存为选择ANSI编码格式
在LoadRunner中需要将txt文件后缀格式转换成dat格式
user.dat
添加数据。
界面说明
点击“Add Column…”,添加新的一列信息,点击“Edit with Notepad”再次编辑参数化数据文件
数据分配与更新方式
Select next row【选择下一行】:
Sequential(顺序):按照参数化的数据顺序,一个一个的来取。
Random(随机):参数化中的数据,每次随机的从中抽取数据。
Unique(唯一):为每个虚拟用户分配一条唯一的数据
Update value on【更新时的值】:
每次迭代(Each iteration) :每次迭代时取新的值,假如50个用户都取第一条数据,称为一次迭代;完了50个用户都取第二条数据,后面以此类推。
每次出现(Each occurrence):每次参数时取新的值,这里强调前后两次取值不能相同。
只取一次(once) :参数化中的数据,一条数据只能被抽取一次。(如果数据轮次完,脚本还在运行将会报错)
组合方式推荐:
--前两种用的多
随机+迭代 : 适用于多点登录,可以搜索、发帖、回帖
唯一+迭代 : 适用于单点登录(不推荐),注册
顺序+迭代:适用于多点登录。可以搜索,发帖,回帖,尽量避免使用
唯一+一次:适用于单点登录
如何参数化?以登录为例
表示使用当前文件的第1列数据进行参数化
表示使用当前文件第2列数据参数化,跟随第1列取值方式。第1列取第1个值第二列就取第2个值
代码中引用
有5组数据可以选择执行5次
代码
Action()
{
//打开首页
web_url("打开首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/login",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//注册型函数放在目标函数的前面
// 提取sessionid,赋给变量sid
web_reg_save_param_ex(
"ParamName=sid",
"LB=JSESSIONID=",
"RB=;",
SEARCH_FILTERS,
"Scope=HEADERS",
LAST);
//登录
//将账号和密码参数化
web_submit_data("登录",
"Action=http://172.16.6.237:8888/WoniuBoss4.0/login/userLogin",
"Method=POST",
"TargetFrame=",
"Referer=",
ITEMDATA,
"Name=userName", "Value={username}", ENDITEM,
"Name=userPass", "Value={passwd}", ENDITEM,
"Name=checkcode", "Value=0000", ENDITEM,
"Name=remember", "Value=Y", ENDITEM,
LAST);
//代码调试,打印出sessionid
lr_error_message(lr_eval_string("{sid}"));
//将提取出来的sessonid添加到cookie中,跨域DOMAIN后面跟服务器的ip,如果不添加的话脚本可能不执行
web_add_cookie("JSESSIONID={sid}; DOMAIN=172.16.6.237");
//登录后首页
web_url("登录后首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/main",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//点击公告发布
web_url("点击公告发布",
"URL= http://172.16.6.237:8888/WoniuBoss4.0/notice/add",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//发布公告
//将标题和内容参数化
web_submit_data("doAdd",
"Action=http://172.16.6.237:8888/WoniuBoss4.0/notice/doAdd",
"Method=POST",
"TargetFrame=",
"RecContentType=text/plain",
"Referer=http://172.16.6.237:8888/WoniuBoss4.0/notice/add",
"Snapshot=t10.inf",
"Mode=HTML",
ITEMDATA,
"Name=title", "Value={notic_title}", ENDITEM,
"Name=content", "Value={notic_contnet}", ENDITEM,
LAST);
return 0;
}
注册型检查函数
//Search后面表示从哪获得查询文本
//Text表示要查询的文本。如果响应错误不再执行下面的请求
web_reg_find("Search=Body",
"Text=success",
LAST);
设置检查点
检查用户登录是否成功
Action()
{
//打开首页
web_url("打开首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/login",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//注册型函数放在目标函数的前面
// 提取sessionid,赋给变量sid
web_reg_save_param_ex(
"ParamName=sid",
"LB=JSESSIONID=",
"RB=;",
SEARCH_FILTERS,
"Scope=HEADERS",
LAST);
//设置检查点,就是断言
//注册型函数要放在前面
//检查用户登录是否成功,如果不成功,脚本不在执行其他请求
web_reg_find("Search=Body",
"Text=success",
LAST);
//登录
//将账号和密码参数化
web_submit_data("登录",
"Action=http://172.16.6.237:8888/WoniuBoss4.0/login/userLogin",
"Method=POST",
"TargetFrame=",
"Referer=",
ITEMDATA,
"Name=userName", "Value={username}", ENDITEM,
"Name=userPass", "Value={passwd}", ENDITEM,
"Name=checkcode", "Value=0000", ENDITEM,
"Name=remember", "Value=Y", ENDITEM,
LAST);
//代码调试,打印出sessionid
lr_error_message(lr_eval_string("{sid}"));
//将提取出来的sessonid添加到cookie中,跨域DOMAIN后面跟服务器的ip,如果不添加的话脚本可能不执行
web_add_cookie("JSESSIONID={sid}; DOMAIN=172.16.6.237");
//登录后首页
web_url("登录后首页",
"URL=http://172.16.6.237:8888/WoniuBoss4.0/main",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//点击公告发布
web_url("点击公告发布",
"URL= http://172.16.6.237:8888/WoniuBoss4.0/notice/add",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//发布公告
//将标题和内容参数化
web_submit_data("doAdd",
"Action=http://172.16.6.237:8888/WoniuBoss4.0/notice/doAdd",
"Method=POST",
"TargetFrame=",
"RecContentType=text/plain",
"Referer=http://172.16.6.237:8888/WoniuBoss4.0/notice/add",
"Snapshot=t10.inf",
"Mode=HTML",
ITEMDATA,
"Name=title", "Value={notic_title}", ENDITEM,
"Name=content", "Value={notic_contnet}", ENDITEM,
LAST);
return 0;
}
检查成功
函数
web_add_header();
//添加信息头
web_add_header("Content-Type",
"multipart/form-data");
手写
Action()
{
//提取cookie
web_reg_save_param_ex(
"ParamName=lastact",
"LB=19LN_2132_lastact=",
"RB=;",
SEARCH_FILTERS,
"Scope=HEADERS",
LAST);
web_url("首页",
"URL=http://172.16.6.125/bbs/forum.php",
"TargetFrame=",
"Resource=1",
"Referer=",
"Mode=HTML",
LAST);
//lr_error_message(lr_eval_string("{lastact}"));
//cookie管理器
web_add_cookie("19LN_2132_lastact={lastact}; DOMAIN=172.16.6.125");
//提取注册formhash
web_reg_save_param_ex(
"ParamName=reformhash",
"LB=,
"RB=\" />",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
//提取name
web_reg_save_param_ex(
"ParamName=name",
"LB=",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
//提取密码
web_reg_save_param_ex(
"ParamName=pwd",
"LB=",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
//提取确认密码
web_reg_save_param_ex(
"ParamName=new_pwd",
"LB=",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
//提取email
web_reg_save_param_ex(
"ParamName=email",
"LB=",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
web_url("打开注册页面",
"URL=http://172.16.6.125/bbs/member.php?mod=register",
"TargetFrame=",
"Resource=0",
"Referer=",
"Mode=HTTP",
LAST);
//打印formhash
lr_error_message(lr_eval_string("{reformhash}"));
//R9D7GV,MYNNcO,a9r9Q9,xJI2R1会失效,应该在打开注册页面去提取
web_submit_data("注册",
"Action=http://172.16.6.125/bbs/member.php?mod=register&inajax=1",
"Method=POST",
"TargetFrame=",
"Referer=",
"Mode=HTTP",
ITEMDATA,
"Name=regsubmit", "Value=yes", ENDITEM,
"Name=formhash", "Value={reformhash}", ENDITEM,
"Name=referer", "Value=http://172.16.6.125/bbs/./", ENDITEM,
"Name=activationauth", "Value=", ENDITEM,
"Name={name}", "Value=lala", ENDITEM,
"Name={pwd}", "Value=123456", ENDITEM,
"Name={new_pwd}", "Value=123456", ENDITEM,
"Name={email}", "[email protected]", ENDITEM,
LAST);
return 0;
}
教程:https://www.cnblogs.com/auguse/articles/13973369.html
什么是事物?
事务(Transaction)是指用户在客户端做一种或多种业务所需要的操作集,通过事务函数可以标记完成该业务所需要的操作内容;另一方面可以用来统计用户操作的相应时间。事务响应时间是指通过记录用户请求的开始时间和服务器返回内容到客户时间的差值来计算用户操作响应时间的。
loadrunner如何划分事物?
1、添加事物
插入事务操作可以在录制脚本过程中,也可以在录制结束后进行。可以在脚本中找到需要添加事务的部分,直接插入:
lr_start_transaction("事务");
lr_end_transaction("事务",LR_AUTO);
注意:1、开始与结束函数必须成对出现
2、事务的名称必须一样。
当然,我们也可以将鼠标定位到要插入事务的位置,通过菜单栏来插入事务(insert—>start transaction/end transaction)
2、事物状态详解
LR事务四种状态,在默认情况下使用LR_AUTO来作为事务状态:
--使用LR_AUTO即可
LR_AUTO:是指事务的状态有系统自动根据默认规则来判断,结果为PASS/FAIL。
LR_PASS:是指事务是以PASS状态通过的,说明该事务正确的完成了,并且记录下对应的时间,这个时间就是指做这件事情所需要消耗的响应时间。
LR_FAIL:是指事务以FAIL状态结束,该事务是一个失败的事务,没有完成事务中脚本应该达到的效果,得到的时间不是正确操作的时间,这个时间在后期的统计中将被独立统计。
LR_STOP:将事务以STOP状态停止。事务的PASS和FAIL状态会在场景的对应计数器中记录,包括通过的次数和事务的响应时间,方便后期分析该事务的吞吐量以及响应时间的变化情况。
代码
Action()
{
lr_start_transaction("注册事务");
//提取cookie
web_reg_save_param_ex(
"ParamName=lastact",
"LB=19LN_2132_lastact=",
"RB=;",
SEARCH_FILTERS,
"Scope=HEADERS",
LAST);
web_url("首页",
"URL=http://172.16.6.125/bbs/forum.php",
"TargetFrame=",
"Resource=1",
"Referer=",
"Mode=HTML",
LAST);
//lr_error_message(lr_eval_string("{lastact}"));
//cookie管理器
web_add_cookie("19LN_2132_lastact={lastact}; DOMAIN=172.16.6.125");
//提取注册formhash
web_reg_save_param_ex(
"ParamName=reformhash",
"LB=,
"RB=\" />",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
web_url("打开注册页面",
"URL=http://172.16.6.125/bbs/member.php?mod=register",
"TargetFrame=",
"Resource=0",
"Referer=",
"Mode=HTTP",
LAST);
//打印formhash
lr_error_message(lr_eval_string("{reformhash}"));
//R9D7GV,MYNNcO,a9r9Q9,xJI2R1会失效,应该在打开注册页面去提取
web_submit_data("注册",
"Action=http://172.16.6.125/bbs/member.php?mod=register&inajax=1",
"Method=POST",
"TargetFrame=",
"Referer=",
"Mode=HTTP",
ITEMDATA,
"Name=regsubmit", "Value=yes", ENDITEM,
"Name=formhash", "Value={reformhash}", ENDITEM,
"Name=referer", "Value=http://172.16.6.125/bbs/./", ENDITEM,
"Name=activationauth", "Value=", ENDITEM,
"Name=R9D7GV", "Value=cddj5", ENDITEM,
"Name=MYNNcO", "Value=123456", ENDITEM,
"Name=a9r9Q9", "Value=123456", ENDITEM,
"Name=xJI2R1", "[email protected]", ENDITEM,
LAST);
lr_end_transaction("注册事务",LR_AUTO);
return 0;
}
教程:https://www.cnblogs.com/auguse/articles/13973714.html
什么是思考时间
用户访问某个网站,例如一次查询,用户需要时间查看查询的结果是否是自己想要的。例如一次订单提交,用户需要时间核对自己填写的信息是否正确等。也就是说用户在做某些操作时,是会有停留时间的,我把这个时间叫思考时间。但利用代码去执行的时候是没有时间的,当然,脚本运行本身是需要时间的,但比起人的思考时间要小很多。这也是我们为什么要用软件来代替人的某些工作。但有时候,我们在做性能测试时,为了更真实的模拟用户的操作,需要给代码加入思考时间。
loadrunne如何设置思考时间
lr_think_time(13);
思考时间:两个步骤之间的停顿时间
注意:
一般在测试过程中(控制台),需要设置思考时间,思考时间的设置根据测试需求来定;
而在脚本生成器中一般 忽略思考时间,越快越好,这是为了方便调试脚本。
点击Run-time Setting下面的TinkTime,设置项详解如下:
Action()
{
lr_start_transaction("注册");
web_url("论坛首页",
"URL=http://172.16.6.125/bbs/forum.php",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t10.inf",
"Mode=HTML",
EXTRARES,
"Url=static/image/common/scrolltop.png", "Referer=http://172.16.6.125/bbs/data/cache/style_1_common.css?oX2", ENDITEM,
LAST);
lr_think_time(7);
//提取formhash
web_reg_save_param_ex(
"ParamName=regformhash",
"LB=,
"RB=\" />",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
//用户名
web_reg_save_param_ex(
"ParamName=name",
"LB=",
SEARCH_FILTERS,
"Scope=BODY",
LAST);
//密码
web_reg_save_param_ex(
"ParamName=pwd",
"LB=
教程:https://www.cnblogs.com/auguse/articles/13973530.html
语法
int web_url( const char *StepName, const char *url, <List of Attributes>, [EXTRARES, <List of Resource Attributes>,] LAST );
参数说明
URL:请求的URL地址
RecContentType:响应头中ContentType的值
Referer:上一步的url地址
Mode : 录制级别:HTML或HTTP
Resource:代表 URL是否是资源
0 代表不是资源
1代表是资源
TargetFrame:包含当前链接或资源的框架的名称。 点击这里查看更多信息。
语法:
int web_submit_data( const char *StepName, const char *Action, <List of Attributes>, ITEMDATA, <List of data>, [ EXTRARES, <List of Resource Attributes>,] LAST );
参数说明:
Action:url地址
Method:请求的类型
EncType:post请求提交的Body格式
RecContentType:响应头中ContentType的值
Referer:上一步的url地址
Mode : 录制级别:HTML或HTTP
Resource:代表 URL是否是资源
0 代表不是资源
1代表不是资源
TargetFrame:包含当前链接或资源的框架的名称。 点击这里查看更多信息。
ITEMDATA:里面为提交的参数,格式:
"Name=ctivationauth","Value=",ENDITEM,
注册型函数,要放在提取内容的提前前面,用来做关联操作
语法:
int web_reg_save_param_ex( const char *ParamName, [const char *LB, ][const char *RB,] <List of Attributes>, <SEARCH FILTERS>,LAST );
参数
//关联
web_reg_save_param_ex(
"ParamName=formhash2",
"LB=
lr_save_string函数可以将,将内容赋值一个变量。
使用web_reg_save_param_ex 提取出多个值赋给fids,保存到变量fid,调试代码时应该打印fid。
//从fids里面随机取出一个值,保存给变量fid
lr_save_string(lr_paramarr_random("fids"),"fid");
//调试代码
lr_error_message(lr_eval_string("{fid}"));
lr_error_message(lr_eval_string("{formhash1}"));
//Search后面表示从哪获得查询文本
//Text表示要查询的文本。如果响应错误不再执行下面的请求
web_reg_find("Search=Body",
"Text=success",
LAST);
将提取出来的sessonid添加到cookie中,跨域DOMAIN后面跟服务器的ip,如果不添加的话脚本可能不执行
web_add_cookie("JSESSIONID={sid}; DOMAIN=172.16.6.237");
//添加信息头
web_add_header("Content-Type",
"multipart/form-data");
lr_start_transaction("事务");
lr_end_transaction("事务",LR_AUTO);
lr_think_time(13);
可以发出 get post put delete 类型的请求,一般如果请求的消息体格式json就是
web_custom_request("登录",
"URL=http://192.168.246.133/bbs/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1",
"Method=POST",
"TargetFrame=",
"Resource=0",
"Referer=http://192.168.246.133/bbs/forum.php",
"EncType=application/x-www-form-urlencoded",
"Body=fastloginfield=username&username=admin&password=123456&quickforward=yes&handlekey=ls",
"RecContentType=text/xml",
LAST);
Body:有多个参数时中间使用$隔开,
教程:https://www.cnblogs.com/auguse/articles/13951019.html
1.备份(针对所有CentOS可用,备份文件在当前路径下)
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
2.安装wget
yum install -y wget
3.下载CentOS 7的repo文件
本人使用的阿里云第二个地址
1.阿里云源:
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
#或者
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
2.网易云源:
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo
4.更新镜像源
1.#清除缓存
yum clean all
2.#生成缓存
yum makecache
3.更新yum源,等待更新完毕即可。(执行加载完后会卡顿一会)
yum -y update
4.yum clean all
5. yum makecache
上传文件至tmp目录下
1.进入tmp,解压到opt目录下。
--注意版本,别傻乎乎直接复制
cd /tmp
tar -zxvf jdk-8u211-linux-x64.tar.gz -C /opt
2.进入opt目录将jdk改名
cd /opt
mv jdk1.8.0_211 jdk1.8
1.进入文件
vi /etc/profile
2.将下面的内容添加在末尾
# JAVA_HOME
export JAVA_HOME=/opt/jdk1.8
export PATH=$PATH:$JAVA_HOME/bin
3.执行命令使配置生效
source /etc/profile
4.检测安装是否成功
java
javac
java -version
由于是负载均衡,暂时安装2个Tomcat8
上传至tmp目录下
1.进入tmp目录,解压至opt目录下
cd /tmp
tar -zxvf apache-tomcat-8.5.55.tar.gz -C /opt
2.进入opt目录,改名改为tomcat1
cd /opt
mv apache-tomcat-8.5.55 tomcat1
3.启动tomcat
/opt/tomcat1/bin/startup.sh
# 查询端口是否开放
firewall-cmd --query-port=8080/tcp
# 开放8080端口
firewall-cmd --permanent --add-port=8080/tcp
#更新防火墙规则
firewall-cmd --reload
别傻乎乎复制,注意自己的ip和端口
http://172.16.6.117:8080/
复制tomcat
1.进入opt目录,复制tomcat1并重命名为tomcat2
cd /opt
cp -r tomcat1 tomcat2
修改第2个tomcat配置文件
需要注意的是多台tomcat配置在同一个机器上避免冲突,需要修改端口
1.进入server.xml
vi /opt/tomcat2/conf/server.xml
启动
/opt/tomcat2/bin/startup.sh
开放8081端口
# 查询端口是否开放
firewall-cmd --query-port=8081/tcp
# 开放8081端口
firewall-cmd --permanent --add-port=8081/tcp
#更新防火墙规则
firewall-cmd --reload
访问
http://172.16.6.117:8081/
为了体现出各个Tomcat的区别,顺便修改一下各个tomcat的index.jsp文件用于区分tomcat
进入第1个Tomcat,找到h1标签
vi /opt/tomcat1/webapps/ROOT/index.jsp
再次访问第1个tomcat就发现不一样了
进入第2个tomcat修改,与上面步骤一样
vi /opt/tomcat2/webapps/ROOT/index.jsp
1.先给文件拥有者给 执行权限 x
chmod +x /etc/rc.d/rc.local
2.进入rc.local
vi /etc/rc.d/rc.local
3.在touch开头的下一行,写上需要开机启动的命令
export JAVA_HOME=/opt/jdk1.8
/opt/tomcat1/bin/startup.sh start
/opt/tomcat2/bin/startup.sh start
Nginx 不在默认的 yum 源中,可以使用 epel 或者官网的 yum 源,本例使用官网的 yum 源。
rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
安装完 yum 源之后,可以查看一下。
yum repolist
下面是安装成功的显示信息
已加载插件:fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.huaweicloud.com
* extras: mirrors.huaweicloud.com
* updates: mirrors.aliyun.com
源标识 源名称 状态
base/7/x86_64 CentOS-7 - Base 10,070
extras/7/x86_64 CentOS-7 - Extras 413
mysql-connectors-community/x86_64 MySQL Connectors Community 175
mysql-tools-community/x86_64 MySQL Tools Community 120
mysql56-community/x86_64 MySQL 5.6 Community Server 564
nginx/x86_64 nginx repo 206
updates/7/x86_64 CentOS-7 - Updates 1,134
repolist: 12,682
yum install nginx
本次执行启动服务和设置开机启动命令
1.启动服务
systemctl start nginx
2.设置开机启动
systemctl enable nginx
3.重启
systemctl restart nginx
4.停止服务
sudo systemctl stop nginx
5.重新加载,因为一般重新配置之后,不希望重启服务,这时可以使用重新加载。
sudo systemctl reload nginx
默认 CentOS7 使用的防火墙 firewalld 是关闭 http 服务的(打开 80 端口)。
firewall-cmd --zone=public --permanent --add-service=http
firewall-cmd --reload
打开之后,可以查看一下防火墙打开的所有的服务
firewall-cmd --list-service
出现下面结果可以看到,系统已经打开了 http 服务。
ssh dhcpv6-client http
Nginx 是一个很方便的反向代理,需要指出的是 CentOS 7 的 SELinux,使用反向代理需要打开网络访问权限。打开网络权限之后,反向代理可以使用了。Nginx安装文件默认在/etc/nginx目录下。
setsebool -P httpd_can_network_connect on
直接访问服务器IP,若出现以下信息则说明到这里我们的Nginx就安装成功了!
注意自己的ip
http://172.16.6.117/
修改Nginx配置文件,注意特别注意:里面的内容全部删除,更换为下面的内容。
vi /etc/nginx/nginx.conf
注意下面的ip,每个人都不一样,别傻乎乎直接复制
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
# 各工程最大连接数
worker_connections 1024;
}
http {
# upstream 各服务器地址以及权重,权重越大代表访问率越大
upstream alie.com {
server 172.16.6.117:8081 weight=1;
server 172.16.6.117:8080 weight=1;
}
server {
# 端口默认80
# 配置域名,由于没有域名,因此使用localhost
server_name localhost;
location / {
# 反向代理,这里的地址与上面配置的upstream需一致,实现负载均衡
proxy_pass http://alie.com;
proxy_redirect default;
}
}
}
重启Nginx并进行访问测试
systemctl reload nginx
直接访问服务器的80端口,Nginx便会通过反向代理将请求转发至配置好的服务器集群中,通过不断刷新即可发现可以访问不同的tomcat。
http://172.16.6.117/
yum install epel-release
在centos7.3默认安装readis后配置文件在/etc/目录下
yum install redis
本次执行启动服务和设置开机自启命令
1.启动服务
systemctl start redis
2.设置开机自启动
systemctl enable redis
3.停止服务
systemctl stop redis
4.重启服务
systemctl restart redis
5.查看服务状态
systemctl status redis
6.查看redis进程
ps -ef | grep redis
7.查看端口
netstat -lnp|grep 6379
vi /etc/redis.conf
修改配置文件redis.conf 大概在55行左右。修改bind 后面的ip为 0.0.0.0 即可
通过TomcatClusterRedisSessionManager,这种方式支持redis3.0的集群方式,下载tomcat-cluster-redis-session-manager.zip包,注意:在github要下载最新版的包
将下面4个jar包上传到tomcat1/lib和tomcat1/lib下,直接覆盖原来的jar包
先关闭Tomcat
关闭第1个
/opt/tomcat1/bin/shutdown.sh
关闭第2个
/opt/tomcat2/bin/shutdown.sh
分别配置2个tomcat的context.xml
vi /opt/tomcat1/conf/context.xml
vi /opt/tomcat2/conf/context.xml
将下面的内容放在
,参考下图中的位置
host="127.0.0.1"
port="6379"
password="" # 如果设置了密码则在这里配置上
database="0" # redis默认16个database 第一个是0 最后一个是15
maxInactiveInterval="60" />
配置会话到期时间
分别配置2个tomcat的web.xml
进入下面文件后,输入命令:/session进行搜索
vi /opt/tomcat1/conf/web.xml
vi /opt/tomcat2/conf/web.xml
将会话到期时间30改为60
/opt/tomcat1/bin/startup.sh
/opt/tomcat2/bin/startup.sh
在tomcat1/webapps/ROOT和tomcat2/webapps/ROOT新建文件session.jsp,然后将下面内容粘贴进去。注意内容上下都要修改
vi tomcat1/webapps/ROOT/session.jsp
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
tomcat-1
Session serviced by tomcat
Session ID
<%=session.getId() %>
<% session.setAttribute("abc","abc");%>
Created on
<%= session.getCreationTime() %>
tomcat-1
vi tomcat2/webapps/ROOT/session.jsp
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
tomcat-2
Session serviced by tomcat
Session ID
<%=session.getId() %>
<% session.setAttribute("abc","abc");%>
Created on
<%= session.getCreationTime() %>
tomcat-2
http://172.16.6.117/session.jsp
tomcat-1节点与tomcat-2节点配置相同,测试,我们每次强刷他的sessionID都是一致的,所以我们认为他的session会话保持已经完成,你们也可以选择换个客户端的IP地址来测试
教程:https://www.cnblogs.com/auguse/articles/13957658.html
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存溢出(Out Of Memory,简称OOM)是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。此时程序就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件,而由系统配置、数据流、用户代码等原因而导致的内存溢出错误,即使用户重新执行任务依然无法避免 [1] 。
}
}
### 2.重启
重启Nginx并进行访问测试
systemctl reload nginx
### 3.访问
直接访问服务器的80端口,Nginx便会通过反向代理将请求转发至配置好的服务器集群中,通过不断刷新即可发现可以访问不同的tomcat。
http://172.16.6.117/
## 7.redis安装
### 1.下载fedora的epel仓库
yum install epel-release
### 2.安装redis数据库
在centos7.3默认安装readis后配置文件在/etc/目录下
yum install redis
### 3.常见命令介绍
本次执行启动服务和设置开机自启命令
1.启动服务
systemctl start redis
2.设置开机自启动
systemctl enable redis
3.停止服务
systemctl stop redis
4.重启服务
systemctl restart redis
5.查看服务状态
systemctl status redis
6.查看redis进程
ps -ef | grep redis
7.查看端口
netstat -lnp|grep 6379
### 4.修改redis配置文件
vi /etc/redis.conf
修改配置文件redis.conf 大概在55行左右。修改bind 后面的ip为 0.0.0.0 即可
[外链图片转存中...(img-XkvTP79b-1631522726398)]
## 8.配置tomcat session redis同步
### 1.上传jar包
通过TomcatClusterRedisSessionManager,这种方式支持redis3.0的集群方式,下载tomcat-cluster-redis-session-manager.zip包,**注意:在github要下载最新版的包**
> 将下面4个jar包上传到tomcat1/lib和tomcat1/lib下,直接覆盖原来的jar包
[外链图片转存中...(img-Vn4M5qdi-1631522726400)]
### 2.配置context.xml
> 先关闭Tomcat
关闭第1个
/opt/tomcat1/bin/shutdown.sh
关闭第2个
/opt/tomcat2/bin/shutdown.sh
> 分别配置2个tomcat的context.xml
vi /opt/tomcat1/conf/context.xml
vi /opt/tomcat2/conf/context.xml
将下面的内容放在`标签里面配置`,参考下图中的位置
host="127.0.0.1" port="6379" password="" # 如果设置了密码则在这里配置上 database="0" # redis默认16个database 第一个是0 最后一个是15 maxInactiveInterval="60" />
[外链图片转存中...(img-IrHLxcnQ-1631522726423)]
### 3.配置web.xml
配置会话到期时间
> 分别配置2个tomcat的web.xml
进入下面文件后,输入命令:/session进行搜索
vi /opt/tomcat1/conf/web.xml
vi /opt/tomcat2/conf/web.xml
将会话到期时间30改为60
[外链图片转存中...(img-uYxD7djJ-1631522726425)]
### 4.启动tomcat
/opt/tomcat1/bin/startup.sh
/opt/tomcat2/bin/startup.sh
### 5.测试,增加文件
> 在tomcat1/webapps/ROOT和tomcat2/webapps/ROOT新建文件session.jsp,然后将下面内容粘贴进去。注意内容上下都要修改
vi tomcat1/webapps/ROOT/session.jsp
<%@page language=“java” import=“java.util.*” pageEncoding=“UTF-8”%>
tomcat-1Session ID | <%=session.getId() %> |
Created on | <%= session.getCreationTime() %> |
vi tomcat2/webapps/ROOT/session.jsp
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
tomcat-2
Session serviced by tomcat
Session ID
<%=session.getId() %>
<% session.setAttribute("abc","abc");%>
Created on
<%= session.getCreationTime() %>
tomcat-2
http://172.16.6.117/session.jsp
tomcat-1节点与tomcat-2节点配置相同,测试,我们每次强刷他的sessionID都是一致的,所以我们认为他的session会话保持已经完成,你们也可以选择换个客户端的IP地址来测试
[外链图片转存中…(img-GhUCD0Z5-1631522726427)]
[外链图片转存中…(img-faOwHi6f-1631522726429)]
教程:https://www.cnblogs.com/auguse/articles/13957658.html
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存溢出(Out Of Memory,简称OOM)是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。此时程序就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件,而由系统配置、数据流、用户代码等原因而导致的内存溢出错误,即使用户重新执行任务依然无法避免 [1] 。