通过引入外部jar包,引用相关签名算法,如rsa、ecdsa等
通过config.Arguments类的getArgument(0).getValue()获取要发送请求的json body体。调用外部jar包方法进行sha等运算
http header的rmv和add操作
import com.matrix.common.service.SignatureService;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.config.Arguments;
//获取签名头
public static String getSign(String contentSHA, String date,String nonce ){
String privateKey = vars.get("privateKey");
String method = "GET";
String accept = vars.get("accept");
String contentType = vars.get("contentType");
String nonceHeader = "x-bfs-signature-nonce:"+nonce;
String url = vars.get("createRfqOrderUrl");
//调用jar包中SignatureService的sign方法进行进行签名
String signature = SignatureService.sign(privateKey,method,accept,contentSHA, contentType, date, nonceHeader, url);
return signature;
}
//发送请求前增加签名相关的Header参数
public static void addHeaders( ){
//增加日期头
String date = (new Date()).toString();
sampler.getHeaderManager().removeHeaderNamed("Date");
sampler.getHeaderManager().add(new Header("Date",date));
//增加nonce头
String nonce = "550e8400e29b41d4a716446655440000";
sampler.getHeaderManager().removeHeaderNamed("x-bfs-signature-nonce");
sampler.getHeaderManager().add(new Header("x-bfs-signature-nonce",nonce));
//通过getArgument方法获取json body
Arguments arguments = sampler.getArguments();
String content = arguments.getArgument(0).getValue();
log.info("content="+content);
//增加Content-SHA256 头
String contentSHA = SignatureService.getContentSHA256(content);
log.info("contentSHA="+contentSHA);
sampler.getHeaderManager().removeHeaderNamed("Content-SHA256");
sampler.getHeaderManager().add(new Header("Content-SHA256",contentSHA));
//增加签名头
String signature = getSign(contentSHA,date,nonce);
sampler.getHeaderManager().removeHeaderNamed("Authorization");
sampler.getHeaderManager().add(new Header("Authorization","bfs "+ vars.get("accessKeyId") +":"+signature));
return;
}
addHeaders();
#############################
在利用jmeter进行接口测试或者性能测试的时候,我们需要处理一些复杂的请求,此时就需要利用beanshell脚本了,BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法,所以它和java是可以无缝衔接的。beanshell由于内置了一些特有的变量,没法在一些集成代码工具上去调试。今天就和大家分享下常用的2个代码例子,大家可以直接拿来稍微修改下就可以使用。
Bean Shell内置变量大全
首先大家需要掌握bean shell本身自带的一些内置变量和一些方法。JMeter在其BeanShell中内置了变量,用户是通过这些变量与JMeter进行交互。
1.log 打印日志,写入信息到jmeber.log文件。
2.SampleResult 获取SampleResult对象,能通过这个对象获取想要的信息。
3.Response 获取Response对象,能通过这个对象获取响应信息。
4.Failure 查看接口调使用能否成功,假如返回false是成功的,true是失败的。
5.FailureMessage 失败信息,没有设置的时候失败信息是空的,能set这个信息。
6.ResponseData 获取response body类型是byte[]。
7.ResponseCode 返回接口code成功是200。
8.ResponseMessage 获取msg成功是OK。
9.ResponseHeaders 获取接口服务端返回的头部信息。
10.RequestHeaders 获取用户端请求的头部信息。
11.SampleLabel 获取接口请求的名称。
12.SamplerData 获取请求的url和body。
13.ctx 代表上下文信息,能直接用。
14.vars即JMeterVariables,操作jmeter变量,这个变量实际引用了JMeter线程中的局部变量容器(本质上是Map),常用方法:
a) vars.get(String key):从jmeter中获得变量值;
b) vars.put(String key,String value):数据存到jmeter变量中;
15.prev 获取前面的sample返回的信息,常用方法:
a) getResponseDataAsString():获取响应信息。
b) getResponseCode() :获取响应code。
在写代码的时候,可以直接用上面的变量和方法,参考如下例子。
使用jmeter的bean shell preprocessor,调用自己写的工具类,实现请求的处理。
步骤如下:
1、 用eclipse把自己写的java代码打包成jar。
2、 把自定义的JAR包放入到jmeter的/lib/ext目录下。
3、 重启jmeter,开始写beanshell脚本。
具体代码:
import net.sf.json.JSONObject;
import ht.test.PluginTest.*;
注意:在用system.out.println调试完成后,最终需要注射掉,否则在真正压测的时候会影响压测客户端的性能。
使用beanshell的assertion断言,完成一些响应结果判断的的操作。
一个比较完整的代码框架如下,大家依葫芦画瓢就行了。
import org.apache.jmeter.assertions;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.assertions.AssertionResult;
import org.json.*;
String response_data = prev.getResponseDataAsString();
高级用法:假如我们希望线程之间共享一个变量,可以利用BeanShell来完成。
JMeter中线程间共享变量可以通过定义属性值来完成,JMeter启动时会读取一些属性文件,比如jmeter.properties、user.properties,这些属性值是可以修改的,通过BeanShell可以对其进行更改。通过props.put()来修改属性,props.get()来获取属性。
Jmeter有哪些Bean Shell
定时器: BeanShell Timer
前置处理器:BeanShell PreProcessor
采样器: BeanShell Sampler
后置处理器:BeanShell PostProcessor
断言: BeanShell断言
监听器: BeanShell Listener
下面先讲一讲 采样器:BeanShell Sampler的用法
在BeanShell Sampler里面写入vars.get和vars.put
查看结果树:
在BeanShell Sampler里面添加方法
查看结果树
引入java文件:
在BeanShell Sampler里面引入java文件
查看结果树
引入class文件:
查看结果树
给自定义的变量赋值
Bean Shell的脚本将test的值传给us
HTTP Request 使用参数化引用us的值
查看结果树:
BeanShell PostProcessor提取json数据
需求:提取sample返回json数据中所有name字段值,返回的json格式如下:
{“body”:{“apps”:[{“name”:”111”},{“name”:”222”}]}}
jmeter中添加后置处理器BeanShell PostProcessor
import org.json.*;
String response_data = prev.getResponseDataAsString();
JSONObject data_obj = new JSONObject(response_data);
String apps_str = data_obj.get("body").get("apps").toString();
JSONArray apps_array = new JSONArray(apps_str);
String[] result = new String[apps_array.length()];
for(int i=0;i<apps_array.length();i++){
JSONObject app_obj = new JSONObject(apps_array.get(i).toString());
String name = app_obj.get("name").toString();
result[i] = name;
vars.put("result", Arrays.toString(result));
在jmeter的中,断言没法对两个变量的进行对比后判断,只能使用Bean Shell断言来进行,总是有人来问怎么写呢。这里写一个简单的实例吧。
获取某个用户对应的设备个数接口与数据库查询的个数结果进行对比
Bean Shell断言解决方案:
获取某个用户对应的设备个数接口这个普通的Http接口,这里就不就列出了
与Oracle数据库的查询,如下图
先建立JDBC连接配置,进行JDBC查询,然后用正则提取器
再建立Bean Shell断言,如下
这里介绍下。${streamid_matchNr}是jmeter正则个数的统计
if (!"${streamid_matchNr}".equals("${res_1}"))
表示${streamid_matchNr}与${res_1}不相等则运行下面的代码。
区区几行bean shell快速解决问题
在写JMeter脚本的时候经常需要模拟一些数据,大部分情况下模拟数据一般采用“CSV Data Set Config”从CSV文件中取数据。但是使用数据文件的问题是不灵活,有多少虚拟用户可能就得准备多少
测试
数据。比如,某应用的用户注册过程需要提供
手机
号码,如果采用CSV文件,需要测试1000虚拟用户则需要准备1000个手机号码,如果测试过程中要增加虚拟用户数目,则需要准备更多的测试数据,这个过程比较费事。对于某些特殊的有规律的测试数据,我们可以采用动态生成测试数据的方式,比如利用本文介绍的BeanShell。
某手机应用的用户注册过程需要提供手机号码,测试场景中除了用户注册过程中会被用到之外,不会对手机号码产生实际操作行为(比如发送
短信
等),只需要符合在
数据库
中表的定义即可(11位char类型)。
实现过程需要考虑不同的虚拟用户在运行的时候不能产生相同的号码,另外还需要考虑同一个虚拟用户在多次循环执行的情况下也不能产生相同的号码。为了实现上述需求,我们需要有一个标识虚拟用户的ID,以及在多次循环执行的情况下标识的当前循环次数的值。
标识虚拟用户可以通过JMeter的内置function - threadNum来得到,而后者可以通过JMeter提供的计数器来实现,先来看一下我们的脚本的结构。“HTTP请求”需要拿到手机号码发起一个测试请求,该手机号码是从一个名为mobile的JMeter变量里取得的,而该变量是通过“BeanShell PreProcessor”处理之后保存到JMeter的变量里。
BeanShell的实现,具体请看下面的代码注释。
import
java
.text.DecimalFormat;
String strThreadNum = "${__threadNum}"; //取得当前的虚拟用户ID
int thNum = Integer.parseInt(strThreadNum);
String str = "${iterNO}"; //取得该虚拟用户当前的循环次数
int i = Integer.parseInt(str);
int mobileNumLastFive = thNum * 10000 + i;
DecimalFormat df = new DecimalFormat( "0000000000" );
String fullNum = 4 + df.format(mobileNumLastFive); //格式化成4开头的11位手机号码
System.out.println(fullNum);
vars.put("mobile", fullNum); //将手机号码存入名为mobile的变量,该变量可以在“HTTP请求”中用到