jmeter的dubbo插件(包括需要的yaml语法)

功能特性

版本支持

Java

Java 8是必需的,插件代码是基于Java 8开发的。

JMeter

本插件是基于JMeter 5.0+开发的,更低版本的,如3.x,是没有测试过的,不保证能正常运行,建议使用JMeter 5.0+。

Dubbo

支持全系列的dubbo版本:2.5.X、2.6.X、2.7.X,包括变种版本:dubbox。

本插件是基于泛化调用的机制实现的,因此对dubbo的版本及协议有所要求,可以参考官方文档:http://dubbo.apache.org/zh-cn/blog/dubbo-generic-invoke.html,其中有一段:

本文介绍的泛化调用和泛接口实现,都是在原生的 Dubbo 协议之上的。在 2.6.2 版本之前,其他协议如 http/hessian 等是不支持泛化调用的,2.6.3 版本将会对这两个协议的泛化调用做支持。

 

支持配置默认参数

  • 注册中心连接协议
  • 注册中心地址
  • RPC调用协议
  • 接口超时
  • …………

插件提供了以上一系列通用参数的Config Element(类似HTTP请求默认值元件),各个Sampler不需要再重复配置,同时也可以方便的切换各种(环境)配置。

不需要引入接口参数所需的jar包

参数是自定义类时,本插件不需要引入对应的jar包,当然引入jar包也可以,但所对应的调用机制会有所不同

传参使用yaml格式

yaml可读性好

这点很重要,这会强制大家测试的时候,书写的参数格式都是统一美观的。

yaml扩展性更好

json的扩展性有限,比如如果要传递一个Long类型,或者新的Java 8日期对象LocalDate,那么官方插件使用的json格式是没办法的。官方的插件如果要扩展参数类型,就需要转变json格式,比如:

{"param1": {"type": "Long", "value": 123}}

这样显然写起来会很烦琐。

yaml在解析上可以做一些扩展,可以支持更多的类型:

  • 123L,就表示转成Long类型
  • !!java.time.LocalDate 2017-05-01表示将日期2017-05-01转化成LocalDate类型

json对日期的表达上不够好,特别是在没有导接口jar包的情况,这种情况下,没有类字段的类型信息,参数会被映射成Map,对于是否转换成Date类型就无法判定。更进一步,如果日期是要转成LocalDate呢?也是无法判定。

List query(List dateTimeList);

对于这种传参,官方的插件无能为力。

List queryDate(List dateList);

这样的接口,也无法传参。

扩展类型的支持,可以参考扩展类型。

yaml兼容json格式

yaml是json格式的超集,是兼容json格式的,参数同样可以用json格式书写(有细节要求,比如键值对的冒号后面要加一个空格)。但不建议用json来书写参数,原因如前面所述,json的可读性和扩展性均不如yaml。

输入输出更加美观

  • 官方插件在输出结果信息时,没有明确设置UTF-8字符编码(需自行修改JMeter的配置),在某些情况下中文会有乱码。本插件对结果设置了输出编码,不需要额外的配置也能正常显示中文
  • 官方插件接口传参使用的是json格式,而填写json的输入框很窄,使得json参数看起来很不明朗。本插件使用更大的输入框,且输入的参数格式为yaml,参数信息的可读性好
  • 官方插件在输出接口调用结果时,没有对结果的json进行格式化,会造成查看上的不便。本插件对接口结果进行格式化输出,可以更容易查看结果
  • 输出结果对日期进行格式化,更便于查看
  •  

sampler时间优化

不计算起始的连接时间(如连接zookeeper),以接口真正调用的时间开始计算,有利于测试接口的响应速度。

使用

下载相应依赖的jar包,放到JMeter的lib/ext目录。

以下所列jar包,是以JMeter 5.0+为基础,如果不是此版本的JMeter,可能会有所出入。

依赖的jar包

  • curator-client-2.12.0.jar
  • curator-framework-2.12.0.jar
  • curator-recipes-2.12.0.jar
  • gson-2.6.1.jar
  • guava-16.0.1.jar
  • javassist-3.15.0-GA.jar
  • jline-0.9.94.jar
  • log4j-over-slf4j-1.7.5.jar
  • netty-3.7.0.Final.jar
  • netty-all-4.1.25.Final.jar
  • snakeyaml-engine-1.0.jar
  • zkclient-0.1.jar
  • zookeeper-3.4.14.jar

jar包说明

  • jar包的版本并不绝对,也可以尝试使用更高或者更低版本,以上只是参考
  • 如果有使用过官方的插件,应该具有依赖所需的大部分jar包了,只需额外下载snakeyaml-engine-1.0.jar即可
  • 以上部分jar包,只有dubbo 2.7.X才需要,这里就不细致区分了,建议全部下载
  • 除了以上的依赖包,还需要dubbo本身的jar包。JMeter中所使用的dubbo包最好和服务端保持一致,由于各自测试的系统有所差别,这里就不提供dubbo的jar包下载,请自行向服务端的开发人员索要
  • 如果需要其他序列化方式,如kryo,请自行再放入所需的jar包

dubbo版本兼容说明

从2.7版本开始,dubbo属于apache顶级项目,包名也发生了变化,给版本的兼容带来了一些麻烦,尤其是对于dubbox这样的变种版本。为了做到兼容各种dubbo版本,本插件做了比较特别的编译处理。

通常来说,只要JMeter中使用的dubbo的jar包版本和服务端一致就可以,但本插件不支持同时测试两种不同体系dubbo版本的服务端。如果测试的服务端是标准版的不同版本(如一个是2.6.X,一个是2.7.X),那么是可以支持的(因为标准版的dubbo高低版本是可以兼容的)。但如果测试的服务端既有标准版的,又有dubbox这样的变种版本,那么是不支持。因为dubbox的jar包和标准版的jar包并不完全兼容,而插件本身同时只允许使用一个dubbo的jar包,所以无法同时支持。如果既要测dubbox,又要测标准版的dubbo,那么建议使用两套JMeter,各自测试不同的版本。

dubbo版本兼容并没有严格的测试,如果发现有不兼容的情况,请提issue。

接口jar包

不论是否引入接口jar包,本插件都提供了支持,但两者的机制有所不同。不引入接口jar包时,接口参数如果是自定义类型,会被映射成Map类型。而引入jar包时,因为jar包中包含了对应的类,则会映射成具体的类型。

但建议优先使用不引入接口jar包的方式,理由如下:

  • 引入jar包,相当于增加了额外的步骤,有时查找获取jar包也是件麻烦的事情,增加了额外的工作量
  • 引入jar包,也意味着需要定期去更新jar包,特别是对于类新增的字段,如果不更新jar包,新的字段将无法传递到服务端
  • 引入jar包,对应的参数会映射成具体的实现类,本插件中,是使用json方式来转换成对应的类实例,json方式的表达能力较为有限,这可能会导致一些类型的丢失,特别是扩展类型

一定要使用接口jar包就只有一种情况,那就是版本兼容。比如使用的是dubbox这样的变种版本,dubbox的序列化机制,会导致没有提供setter方法的自定义类,无法对private字段赋值(标准版的dubbo是可以的)。如下面的类:

public class Page implements Serializable {
	private int row;
	private int page;

	public Page(int row, int page) {
		this.row = row;
		this.page = page;
	}
}

如果不引入接口jar包,插件会以Map的方式传递参数,服务端在反序列化时,无法将值赋给private字段,导致服务端收到的参数值是无效的。如果是引入jar包,则插件会以具体类实例的方式传递参数,服务端可以正常反序列化。

配置元件

在对dubbo接口进行测试之前,建议先创建一个配置元件。配置元件的配置相当于默认配置,后续的Sampler如无具体配置,则会使用此元件的配置。

各个配置项都对应dubbo相关的参数,如不清楚,请先自行了解dubbo或者向服务端开发人员咨询,这里不再赘述。通常情况下,只需要配置Registry AddressRegistry Protocol(直连选择none)即可。

dubbo接口测试

添加相应的Sampler进行测试:Dubbo Sampler

Sampler中的配置项如果与配置元件相同,则留空即可,不同则自行填写。Sampler中最主要的有以下三项:

  • Interface:对应要测试的接口
  • Method:对应要测试的接口方法
  • Parameter:对应要测试的接口方法参数

InterfaceMethod都很简单明了,Parameter项比较复杂,本插件使用了yaml来表示参数,可读性更好,也更有扩展性,详细使用请参考下面的说明。

参数yaml示例

yaml是json的超集,即参数也可以使用json格式来表示。如果有使用过官方的插件,可以直接进行转换。官方插件是使用每行一个paramTypeparamValue来表示参数,假设填写了3个参数(3行),则可以按以下格式来转换:

[
  {"paramType1": paramValue1的内容},
  {"paramType2": paramValue2的内容},
  {"paramType3": paramValue3的内容}
]

注意paramValue如果是字符串,要加上"

当然,更推荐使用yaml来表示参数。

基本写法

yaml使用-来表示列表,每行对应一个参数:

- 类型名: 类型值
- 类型名: 类型值
- 类型名: 类型值

类型名是用于确定要调用哪个方法,如果有学过Java的同学,就会知道这其实跟通过反射来动态调用类方法原理是一样的。假设有一个类方法签名为:

class OrderService {
    boolean submit(Integer userId, String orderNo) {
        return true;
    }
}

则对应的反射调用代码:

Method method = OrderService.class.getMethod("submit", Integer.class, String.class);
method.invoke(object, args);

可以看到要调用某个方法,必须指明方法的参数类型,因此这里用yaml来表达参数时,必须要有类型名类型值类型名用于确定要调用的方法,等价于上例中的Integer.class, String.class类型值用于给方法传参,等价于上例中的args

还是以OrderService#submit为例,则对应的参数yaml可以写成:

- java.lang.Integer: 1
- java.lang.String: test

表示第一个参数为整型,第二个参数为字符串,正好对应submit方法的参数类型。

值隐式类型

yaml中提供了多种隐式类型(或者可以称为常量结构),包括:整数、浮点数、字符串、null、布尔。以下为常量的基本使用示例:

boolean: 
    - TRUE  #true,True都可以
    - FALSE  #false,False都可以
float:
    - 3.14
    - 6.8523015e+5  #可以使用科学计数法
int:
    - 123
null:
    name: ~  #使用~表示null
    age: null #使用null表示null
    sex: #不写任何东西表示null
string:
    - 哈哈
    - 'Hello world'  #可以使用双引号或者单引号包裹特殊字符
    - "123"	#字符串的123,而非数字123

如果可以通过值来推断出类型,则可以使用隐式类型的写法,如上节的参数示例可以简化为:

- 1
- test

第1个参数只写了类型值1,对应的隐式类型为Integer,第2个参数只写了类型值test,对应的隐式类型为String。正好可以对应submit方法的参数类型,因此这里可以省略类型名,只写类型值

如果参数是基本类型,则无法使用隐式类型。隐式类型只对应对象类型(基本类型的包装类型),如:1对应Integer,而不是基本类型int,如果要使用基本类型作为类型名,需要明确写明类型名

注意:整数本身其实不完全对应Integer类型,而是会根据整数值的大小自动选择类型,如LongBigInteger

值显式类型

有时需要明确指明值为某种类型,可以使用!!来显式的表示类型:

- !!float 54321
- !!str 54321
- !!str true

如上例的543211,如果不加显式类型,则会转为Integer,同理true不加显式类型会转为Boolean,加上显式类型,则都转化对应的类型。

intfloatboolnullstr这几个是自带的显式类型,除此之外,本插件还扩展定义了其他的类型,如longdatejava.time.LocalDate等。

类型名缩写

本插件提供了一些常用类型的名称缩写:

缩写 对应的Java类型
string java.lang.String
str java.lang.String
List java.util.List
list java.util.List
Set java.util.Set
set java.util.Set
Map java.util.Map
map java.util.Map

基本类型直接按基本类型的写法即可:

基本类型 对应的Java类型
int int
double double
short short
float float
long long
byte byte
bool boolean
boolean boolean
char char

如果是java.lang包下的类型,可以不写java.lang包名。如Integer就表示java.lang.IntegerLongDouble等类型也一样。

在写参数时,可以使用这些缩写,如:

- int: 123
- float: 1.2

这里再次强调,类型名是为了确定要调用的方法,一定要明确(否则调用不到方法)。前面说过,类型名可以由值来推断,这只适合值对应的类型刚好完全对应要调用的方法参数类型,实际中,这两者可能是不对应的。

- long: 123

这里写的接口参数类型是long,但传入的值123实际上是Integer类型(通常dubbo底层调用机制是会自动将Integer转为Long的)。

再看一个例子:

void reserve(List seatIds);

这时,参数如果写成:

- [1, 2, 3]

会造成方法调用失败。因为虽然[1, 2, 3]这样的值也是List类型,但其实际类型是List类型的子类ArrayList。如果这样写,则实际调用的方法为:

void reserve(ArrayList seatIds);

显然这个方法是不存在的,应改为:

- list: [1, 2, 3]

加上list后,对应的类型声明就变成了List(值仍然是ArrayList),就可以正确调用到方法了。

请注意,不要混淆类型名值类型类型名是用于确定要调用哪个方法,一旦确定要调用哪个方法,类型名就是确定的,值类型则是传入的参数类型。

假设有如下方法:

interface OrderService {
    List query(Object params);
}

paramsObject类型,这个是确定的,不能写成其他的,不然会调不到方法。但实际传入的参数,可能是各种值类型:

- java.lang.Object: 123 #传入整型
- java.lang.Object: "123" #传入字符串类型
- java.lang.Object: !!str 123 #传入字符串类型
- java.lang.Object: name #传入字符串类型
- java.lang.Object: true #传入布尔型

复杂类型

复杂类型是指参数为自定义类,在yaml使用map来表示这种类型。map的写法为:分隔的键值对,每行一个。

假设要调用的接口方法声明如下:

interface OrderService {
    OrderParamDto query(OrderParamDto orderParamDto, Page page, Integer userId);
}

OrderParamDto类定义:

public class OrderParamDto {
    private String id;
    private Integer price;
    private Long userId;
    private Page page;
    private OrderStatus status;
    // 省略getter/setter
}

public enum OrderStatus {
    ORDER(0),
    PAID(1),
    CANCEL(2),
    FINISH(3);

    private int code;

    OrderStatus(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}

public class Page {
    private int page;
    private int rows;
    private List sort;
    // 省略getter/setter
}

public class Sort {
    private String field;
    private String order;
    // 省略getter/setter
}

参数示例:

- com.yajad.dto.OrderParamDto:
    id: "123"
    price: 100
    status: CANCEL
- com.yajad.dto.Page:
    page: 1
    rows: 2
    sort:
    - 
      field: name3
      order: desc
    - 
      field: name5
      order: asc
- 123
  • 参数是自定义类型时,字段缩进要多一层(4个空格)
  • status为枚举类型,可以使用枚举值对应的字面量字符串来表示

扩展类型

官方插件因为使用json来表示参数,在值类型的支持上不尽人意。例如日期类型很难写(没找到怎么写,尤其是在不导接口jar包调用方法的情况)、不支持LocalDateBigDecimal等类型。

本插件之所以选择yaml来表示参数,很大的一个原因就是yaml的可扩展性,可以自行扩展支持新的类型。

Long类型

长整型在数字后面加Ll即可,如:123L,也可以使用!!long来声明。如:

- 123L
- !!long 123

日期类型

日期类型使用年-月-日格式,如果包含时间,则使用年-月-日 时:分:秒格式:

- 2019-05-01
- !!date 2019-05-01
- 2019-05-01 12:00:01
- !!java.time.LocalDate 2019-05-01
- !!java.time.LocalDateTime 2019-05-01 12:01:02

前3个会转化为Date类型,后2个则转化为对应的LocalDateLocalDateTime类型。

其他类型

!!可以直接使用Java类型,支持有静态方法valueOf的类或构造方法可以传String参数的类进行实例化,如:

- !!java.lang.Float 2019
- !!java.math.BigDecimal 100.05

这种写法,相当于Java代码:

Float.valueOf("2019");
new BigDecimal("100.05");

对于java.lang包下的类,可以只写类名,如:

- !!Float 2019

数组类型

数组类型不论是类型名,还是类型值,只要在类型后面加上[]即可,如:

- int[]: [1,2,3]
- Number[]: [1,2,3]
- !!int[] [1,2,3]
- !!java.math.BigDecimal[]
  - !!java.math.BigDecimal 1.1
  - !!java.math.BigDecimal 2.2
  - !!java.math.BigDecimal 3.3

注意:目前只支持一维数组,多维数组实际使用非常少,暂不考虑

json支持

由于yaml是json的超集,因此参数也可以直接用json格式来表达,甚至可以混合使用。

- com.yajad.dto.OrderParamDto:
    id: '38'
    price: 100
    page:
      page: 1
      rows: 10
      sort:
        field: name
        order: desc
- com.yajad.dto.User:
    id: 38
    name: test

可以直接写成json:

[
  {
    "com.yajad.dto.OrderParamDto": {
      "id": "38",
      "price": 100,
      "page": {
        "page": 1,
        "rows": 10,
        "sort": {
          "field": "name",
          "order": "desc"
        }
      }
    }
  },
  {
    "com.yajad.dto.User": {
      "id": 38,
      "name": "test"
    }
  }
]

混合写法:

- com.yajad.dto.OrderParamDto:
    id: '38'
    price: 100
    page:
      page: 1
      rows: 10
      sort:
        field: name
        order: desc
- com.yajad.dto.User: {id: 38, name: "test"}

混合写法,注意属性和值之间要有一个空格。如上例的,name属性和值test之间有一个空格。

虽然yaml支持json,但还是建议用yaml来书写参数,会有更好的可读性。为了兼顾习惯json的同学,插件提供了一个转换功能,可以将json格式转换为yaml格式。只需点击参数输入框左下角的json to yaml按钮即可转换,注意输入的json中不要含有JMeter变量,否则会被视为不合法的json,将无法转换(可以先将变量写成常量,转换后再写回成变量)。

注意:如果使用json写法,则无法使用扩展类型,这也是推荐使用yaml来写参数的一个原因。

null值

- java.lang.Integer: null
- org.apache.commons.lang3.StringUtils: null
- float: null
- Double: null
- str:

类型值写成null即表示null值,也可以不写,如上面例子中的str:,表示类型是字符串,值是null。

无参数

如果要调用的接口方法不需要传参,那么将参数输入框留空即可。

参考jithub上的

你可能感兴趣的:(个人收藏)