一、JMeter 如何通过自定义Sample来压测RPC服务

RPC(Remote Procedure Call)俗称远程过程调用,是常用的一种高效的服务调用方式,也是性能压测时经常遇到的一种服务调用形式。常见的RPC有GRPC、Thrift、Dubbo等。这里以GRPC为例介绍在JMeter中如何添加自定义的Sample来压测GRPC服务,JMeter中提供的Sample如下图所示,从中可以看到并没有我们需要压测GRPC的Sampler。

本文作者:张永清, 转载请注明: https://www.cnblogs.com/laoqing/p/16339979.html  来源于博客园 ,本文摘选自《软件性能测试分析与调优实践之路》

但是从图中可以看到,JMeter中提供了Java 请求Sample,因此我们可以编写一个自定义的Java请求的Sample来实现GRPC调用,由于需要自定义,自然就需要新建一个Java语言的Maven项目,在项目中引入如下jar包依赖,jar包的版本需要跟压测时的JMeter工具版本保持一致。由于笔者用的JMeter工具的版本是3.0,所以如下依赖包选择的也是3.0版本。由于本节需要一些Java语言和Maven项目管理的基础,所以对于这块不熟悉的读者可以预先阅读一些关于这块的基础书籍。

<dependency><groupId>org.apache.jmeter</groupId><artifactId>ApacheJMeter_java</artifactId><version>3.0</version>
</dependency>

项目中除了需要增加JMeter的依赖外,还需要增加GRPC的依赖,Maven项目完整的pom内容如下所示。

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

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

   <groupId>jmeter.tools</groupId>

    <artifactId>jmeter-grpc</artifactId>

    <packaging>jar</packaging>

    <version>1.0-SNAPSHOT</version>

    <properties>

        <grpc.version>1.27.0</grpc.version>

    </properties>

    <dependencies>

        <dependency>

            <groupId>io.grpc</groupId>

            <artifactId>grpc-netty</artifactId>

            <version>${grpc.version}</version>

        </dependency>

        <dependency>

            <groupId>io.grpc</groupId>

            <artifactId>grpc-protobuf</artifactId>

            <version>${grpc.version}</version>

        </dependency>

        <dependency>

            <groupId>io.grpc</groupId>

            <artifactId>grpc-stub</artifactId>

            <version>${grpc.version}</version>

        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_java -->

        <dependency>

            <groupId>org.apache.jmeter</groupId>

            <artifactId>ApacheJMeter_java</artifactId>

            <version>3.0</version>

        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_core -->

        <dependency>

            <groupId>org.apache.jmeter</groupId>

            <artifactId>ApacheJMeter_core</artifactId>

            <version>3.0</version>

        </dependency>

    </dependencies>

    <build>

        <plugins>

            <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-compiler-plugin</artifactId>

                <configuration>

                    <source>${java.version}</source>

                    <target>${java.version}</target>

                    <skip>true</skip>

                    <encoding>${project.build.sourceEncoding}</encoding>

                </configuration>

            </plugin>

            <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-dependency-plugin</artifactId>

                <version>2.8</version>

                <executions>

                    <execution>

                        <id>copy-dependencies</id>

                        <phase>package</phase>

                        <goals>

                            <goal>copy-dependencies</goal>

                        </goals>

                        <configuration>

                           <outputDirectory>${project.build.directory}</outputDirectory>

                            <overWriteReleases>true</overWriteReleases>

                            <overWriteSnapshots>true</overWriteSnapshots>

                            <overWriteIfNewer>true</overWriteIfNewer>

                            <useSubDirectoryPerType>true</useSubDirectoryPerType>

                            <includeArtifactIds>

                                guava

                            </includeArtifactIds>

                            <silent>true</silent>

                        </configuration>

                    </execution>

                </executions>

            </plugin>

            <plugin>

                <artifactId>maven-assembly-plugin</artifactId>

                <configuration>

                    <appendAssemblyId>false</appendAssemblyId>

                    <descriptorRefs>

                        <descriptorRef>jar-with-dependencies</descriptorRef>

                    </descriptorRefs>

                </configuration>

            </plugin>

        </plugins>

        <defaultGoal>compile</defaultGoal>

    </build>

</project>

编写一个自定义的Java请求Sample,只需要实现JMeter提供的JavaSamplerClient接口即可,如下所示。

本文作者:张永清, 转载请注明: https://www.cnblogs.com/laoqing/p/16339979.html  来源于博客园 ,本文摘选自《软件性能测试分析与调优实践之路》

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

import org.apache.jmeter.config.Arguments;

import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient;

import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;

import org.apache.jmeter.samplers.SampleResult;

public class ExampleSample implements JavaSamplerClient {

    @Override

    public void setupTest(JavaSamplerContext javaSamplerContext) {

        //初始化方法,对数据进行初始化,该方法只会执行一次

    }

    @Override

    public SampleResult runTest(JavaSamplerContext javaSamplerContext) {

        //Sample的请求的具体实现

        return null;

    }

    @Override

    public void teardownTest(JavaSamplerContext javaSamplerContext) {

        //数据或者资源销毁接口,一般用于压测停止时,需要做的动作。

    }

    @Override

    public Arguments getDefaultParameters() {

        //参数设置方法,一般用于设置传递参数

        return null;

    }

}

JMeter提供的JavaSamplerClient接口需要实现的四个方法,如下表所示。

表: JavaSamplerClient接口需要实现的四个方法说明

方法

描述

setupTest(JavaSamplerContext javaSamplerContext)

初始化方法。一般用于对数据进行初始化。性能压测时该方法只会被执行一次,方法体里面的内容可以为空

runTest(JavaSamplerContext javaSamplerContext)

Sample请求的具体实现。比如调用GRPC服务就需要在该方法中编写调用GRPC服务的代码

teardownTest(JavaSamplerContext javaSamplerContext)

用于数据或者资源销毁的方法。一般用于压测停止时,需要执行的数据或者资源的释放动作。性能压测时该方法也只会被执行一次,方法体里面的内容同样可以为空

getDefaultParameters()

参数设置方法。一般用于设置传递的参数

GRPC示例:以传入用户名和密码进行用户注册的GRPC服务作为示例,该GRPC接口请求输入和响应输出都是JSON的文本形式,GRPC服务的proto文件内容如下(proto是GRPC提供的接口协议定义标准文档):

1

2

3

4

5

6

7

8

9

10

11

12

syntax = "proto3";

package com.zyq.example.cas.management.grpc;

message RequestData {

  string text = 1;

}

message ResponseData {

  string text = 1;

}

service StreamService {

  //rpc服务的方法

  rpc SimpleFun(RequestData) returns (ResponseData){}

}

服务接口详细说明如下表示。

表:  服务接口详细说明

参数

说明

RequestData

定义了文本类型的参数用于GRPC服务的请求入参使用,比如传入JSON: {"userAccount":"zyq","password":"mima"}

ResponseData

定义了文本类型的参数用于请求响应使用,用于存储GRPC服务调用后响应的文本内容

StreamService

定义了一个GRPC服务,并且服务里面包含了SimpleFun这个方法,方法中请求传入RequestData,调用完成后返回ResponseData

本文作者:张永清, 转载请注明: https://www.cnblogs.com/laoqing/p/16339979.html  来源于博客园 ,本文摘选自《软件性能测试分析与调优实践之路》

请求调用过程如下图所示。

服务器的配置信息如下表所示。

表:  服务器的配置说明

服务器类型

配置说明

应用服务器(GRPC)

内存:2G

CPU:4核

部署软件:GRPC Java应用服务、JDK1.8

操作系统:CentOS7

数据库服务器

内存:2G

CPU:2核

部署软件:MySQL

操作系统:CentOS7

本文作者:张永清, 转载请注明: https://www.cnblogs.com/laoqing/p/16339979.html  来源于博客园 ,本文摘选自《软件性能测试分析与调优实践之路》

笔者这里自己实现的GRPC服务的Sample具体示例代码如下:

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

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

import com.cf.cas.management.grpc.Example;

import com.cf.cas.management.grpc.StreamServiceGrpc;

import com.google.gson.Gson;

import io.grpc.ManagedChannel;

import io.grpc.ManagedChannelBuilder;

import org.apache.jmeter.config.Arguments;

import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient;

import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;

import org.apache.jmeter.samplers.SampleResult;

import java.util.HashMap;

import java.util.Map;

/**

 * Created by zyq on 2020/3/4.

 */

public class GrpcJmeter implements JavaSamplerClient {

    private String userAccount;

    private String password;

    private String address;

    private Integer port;

    @Override

    public void setupTest(JavaSamplerContext javaSamplerContext) {

    }

    @Override

    public SampleResult runTest(JavaSamplerContext javaSamplerContext) {

        SampleResult results = new SampleResult();

        userAccount = javaSamplerContext.getParameter("userAccount"); // 获取在JMeter中设置的参数值

        password = javaSamplerContext.getParameter("password"); // 获取在JMeter中设置的参数值

        address = javaSamplerContext.getParameter("address"); // 获取在JMeter中设置的参数值

        port =Integer.valueOf(javaSamplerContext.getParameter("port")) ; // 获取在JMeter中设置的参数值

        results.sampleStart();// JMeter 开始统计响应时间标记

        ManagedChannel channel=null;

        try {

            //grpc调用的具体实现

             channel = ManagedChannelBuilder.forAddress(address, port).usePlaintext().build();

            StreamServiceGrpc.StreamServiceBlockingStub stub = StreamServiceGrpc.newBlockingStub(channel);

            Map<String,Object> map = new HashMap<>();

            map.put("userAccount",userAccount);

            map.put("password",password);

            Gson gson = new Gson();

            Example.RequestData requestData = Example.RequestData.newBuilder().setText(gson.toJson(map)).build();

            Example.ResponseData responseData = stub.simpleFun(requestData);

            //设置请求的数据,这里设置后,在JMeter的察看结果树中才可显示

            results.setRequestHeaders(gson.toJson(map));

            if(null!=responseData && null!=responseData.getText() && responseData.getText().contains("success")){

                results.setSuccessful(true);

            }

            else {

                results.setSuccessful(false);

            }

            //设置响应的数据,这里设置后,在JMeter的察看结果树中才可显示

            results.setResponseMessage(responseData.getText());

            results.setResponseData(responseData.getText(),"UTF-8");

        catch (Exception e) {

            results.setSuccessful(false);

            e.printStackTrace();

        }

        finally {

            if(null!=channel){

                channel.shutdown();

            }

            results.sampleEnd();// JMeter 结束统计响应时间标记

        }

        return results;

    }

    @Override

    public void teardownTest(JavaSamplerContext javaSamplerContext) {

    }

    @Override

    public Arguments getDefaultParameters() {

        Arguments params = new Arguments();

        params.addArgument("userAccount""zyq");//设置参数,并赋予默认值

        params.addArgument("password""111");//设置参数,并赋予默认值

        params.addArgument("address""127.0.0.1");//设置参数,并赋予默认值

        params.addArgument("port""8883");//设置参数,并赋予默认值

        return params;

    }

}

  本文作者:张永清, 转载请注明: https://www.cnblogs.com/laoqing/p/16339979.html  来源于博客园 ,本文摘选自《软件性能测试分析与调优实践之路》

示例编写完成后,执行Maven项目打包命令mvn assembly:assembly,即可生成性能压测时需要放入JMeter中的jar包,如下图所示。

将生成的jmeter-grpc-1.0-SNAPSHOT.jar放入JMeter工具的apache-jmeter-3.0\apache-jmeter-3.0\lib\ext目录下,如下图所示,JMeter的ext目录专门用于存放扩展的JMeter自定义jar包。

放入后打开JMeter工具,在添加Java请求Sample后,即可看到我们自己编写的自定义GRPC服务Sample了,如下图所示。

在JMeter工具中执行请求调用后,即可在察看结果树这个JMeter元件中看到请求调用的结果,如下所示。

由此可见,JMeter支持的功能其实非常强大,理论上只要Java语言可以调用的服务都可以使用JMeter来做性能压测。

二、JMeter对GRPC服务的性能压测分析与调优

在添加完GRPC服务的Sample后,我们在上图的基础上,增加Summary Report、聚合报告、图形结果、响应断言、计数器这几个JMeter元件,以辅助我们做性能压测。其中计数器是本次用来辅助做参数化的,如下图所示,在图中userAccount和password这两个参数都用到了计数器产生的counter变量来构造数据,由于计数器是递增的,所以保证了构造出来的数据不会重复。

JMeter的性能压测脚本准备完成后,采用10个并发用户开始进行压测,如下图所示。

 未完待续........(中间省略的部分请查看原书)

使用jvisualvm工具,查看jvm进程的线程运行情况如下图所示。可以看到由于是10个并发用户,所以GRPC服务端的默认执行线程也是10个,但是从图中可以看到这些线程大部分时间都不是处于真正的运行状态,而是处于监视状态,由此怀疑服务端应用程序多线程并发处理时可能遇到了同步锁争抢。

未完待续........(中间省略的部分请查看原书)

从代码中可以看到,这段代码使用同步锁来保证插入到数据中的用户账号不会重复,每次插入前都需要先查询数据库中是否存在该账号,如果不存在才插入,同步锁是用来保证并发调用时线程安全的,确保数据库中不会出现重复的脏数据。

针对上述情况,分析总结如下:

  •         代码中虽然使用了同步锁保证了线程安全,使数据库中不出现重复的脏数据,但是却影响了多线程并发时的性能。而且此种线程安全只能适用单个应用服务器节点的部署情况,如果是分布式的多个节点部署方案,则此种同步锁无法奏效,此时一般需要借助分布式同步锁,比如借助Redis、Zookeeper来实现分布式同步锁。但是使用这种分布式同步锁,其并发性能一般也很低效。
  •         除了使用同步锁来保证数据不重复插入这种方式外,还可以使用数据库的唯一索引来保证数据库的数据唯一。比如针对本示例中的情况,可以对数据库表中的用户账号字段建立唯一索引,确保不重复插入,虽然使用唯一索引后,数据库肯定会有性能消耗,但是在数据量不是非常大的时候,这种方式性能效果应该更佳,而且由于需要根据用户账号查询,所以在查询时,也是需要索引来提高查询效率。
  •         针对数据库中用户表中的数据量非常大的情况,还可以采用分表的方案。比如可以针对用户账号基于某种算法做分表处理,确保同一个用户账号采用算法计算时每次都是进入同一个表中,这样还是可以对每张分表中的用户账号字段建立唯一索引来提高性能。

本文作者:张永清, 转载请注明: https://www.cnblogs.com/laoqing/p/16339979.html  来源于博客园 ,本文摘选自《软件性能测试分析与调优实践之路》

未完待续,更多内容

  

备注:作者的原创文章,转载须注明出处。原创文章归作者所有,欢迎转载,但是保留版权。对于转载了博主的原创文章,不标注出处的,作者将依法追究版权,请尊重作者的成果。

关于软件性能分析调优,可以加微信号yq597365581或者微信号hqh345932,进入专业的性能分析调优群进行交流沟通。

作者的原创文章,转载须注明出处。原创文章归作者所有,欢迎转载,但是保留版权。对于转载了博主的原创文章,不标注出处的,作者将依法追究版权,请尊重作者的成果。

软件性能测试分析与调优实践之路-JMeter对RPC服务的性能压测分析与调优-手稿节选相关推荐

  1. Java性能压测工具及其调优

    Java性能压测工具及其调优 JMeter工具 使用 JVisualVM使用 使用 简介 在日常的开发中,Java的性能显得尤为重要,一个程序的好坏主要就是性能来决定的. JMeter工具 JMete ...

  2. Linux下性能压测之系统参数调优

    1. 前言 在做服务端压测的时候,经常会遇到一些由于Linux系统限制导致压力上不去的情况,这里从Linux系统参数上做一些调优,减少此类因素的干扰. 2. 配置 2.1 Linux系统配置 执行:s ...

  3. RocketMQ性能压测分析(转载)

    一   机器部署 1.1  机器组成 1台nameserver 1台broker  异步刷盘 2台producer 2台consumer 1.2  硬件配置 CPU  两颗x86_64cpu,每颗cp ...

  4. 【转载】软件性能测试分析与调优实践之路-性能分析调优思想与调优技术总结

    本文主要阐述软件性能测试中的一些调优思想和技术,节选自作者新书<软件性能测试分析与调优实践之路>部分章节归纳. 一.  性能分析与调优思想 1.性能分析调优模型 性能测试除了为获取性能指标 ...

  5. 软件性能测试分析与调优实践之路-性能分析调优思想与调优技术总结

    来源:https://www.cnblogs.com/laoqing/p/13660768.html 本文主要阐述软件性能测试中的一些调优思想和技术,节选自作者新书<软件性能测试分析与调优实践之 ...

  6. 软件性能测试、分析与调优实践之路_读书笔记(一)

    简介 : 开始在CSDN记录自己性能测试方面的学习笔记和经验.这本性能测试的书已经看完,开始梳理记录里面的重要知识和实践经验,因为自己记忆力不是很好,总会忘记. 感谢本书作者 ,前人栽树,后人乘凉 ! ...

  7. 《软件性能测试、分析与调优实践之路》简介

    <软件性能测试.分析与调优实践之路>,京东当当天猫都有发售.几十所高校选为教材.所有的软件测试人员.软件开发人员都应该学习一下. 系统或者软件性能的重要性自然是无须多言,永远没有哪个用户可 ...

  8. 软件性能测试分析与调优实践之路-Java应用程序的性能分析与调优-手稿节选

    Java编程语言自从诞生起,就成为了一门非常流行的编程语言,覆盖了互联网.安卓应用.后端应用.大数据等很多技术领域,因此Java应用程序的性能分析和调优也是一门非常重要的课题.Java应用程序的性能直 ...

  9. 【转载】软件性能测试分析与调优实践之路-Web中间件的性能分析与调优总结

    本文主要阐述软件性能测试中的一些调优思想和技术,节选自作者新书<软件性能测试分析与调优实践之路>部分章节归纳. 在国内互联网公司中,Web中间件用的最多的就是Apache和Nginx这两款 ...

最新文章

  1. 【OpenCV 4开发详解】分割图像——Mean-Shift分割算法
  2. python对财务人员的帮助-还不熟练VBA的财务人,让Python带你弯道超车!
  3. 计算机右键管理中没有用户管理,我的电脑右键菜单中没有管理选项如何解决? 我的电脑右键菜单中没有管理选项解决的方法有哪些?...
  4. 针对不同的Cookie做页面缓存
  5. 为什么开不了4g网络_为什么4G网络越来越慢?究竟是你手机出了问题还是网络原因?...
  6. mongodb查询内嵌文档
  7. 成功人士都有的好习惯
  8. 在 npm script 中使用环境变量
  9. 如何打开linux内核选项APIC选项,非标准BIOS启动裁减后的Linux内核的一些问题?
  10. Spring AOP之HelloWorld与概念介绍(xml版)
  11. 我需要完全理解这部分代码才能确保它能够正常工作,如果由我来修复代码中的问题,我是不会这么写的,因此希望你也不要这么来写(转)...
  12. 大学四年,我是怎么靠做外包私活赚了10w+,实现经济独立
  13. 首先,打破一切常规 学习笔记 之五
  14. python123数字转换_Python 中文(大写)数字转阿拉伯数字(转)
  15. 用计算机语言说一局情话,计算机中的情话
  16. 加入域时“找不到网络路径”的解决办法
  17. ORDER: OpenWorld Object Detection on Road Scenes
  18. OPEN(SAP) UI5 学习入门系列之二: 最佳实践练习(上)
  19. 百度地图路线规划重新设置起点、终点图标和路线颜色
  20. 如何简单的爬取网络数据

热门文章

  1. 有功功率、无功功率、视在功率、功率因素
  2. python语言提供的3个基本数字类型是什么_计算机二级python学习教程(3) python语言基本数据类型...
  3. python openpyxl模块的安装
  4. Ubuntu安装JDK并配置JAVA环境变量
  5. 上海sem:关键词优化在SEM营销中的作用
  6. python实现信息熵和条件熵
  7. 苹果手机通话怎么录音?iPhone录音的4个方法!
  8. msdn win10镜像获取
  9. drawRect方法绘图
  10. word字体大小与公式编辑器字体对照表