jmeter压测hprose for dot net实例(对比Webapi)

    科技2023-09-11  102

    使用jmeter压测hprose for dot net实例(基于学生信息crud的服务端hprose对比webapi压力测试)

    提示:在查看这篇文章之前,小伙伴们可能需要看一看我的上一篇文章:Jmeter压测Hprose for dotnet or java

    文章目录

    使用jmeter压测hprose for dot net实例(基于学生信息crud的服务端hprose对比webapi压力测试)前言一、测试用例报告(WebApi)二、具体搭建WebApi1. vs2019创建新项目---ASP.NET Web应用程序---配置新项目---创建---WebAPI2. 新建Modules文件夹,建立student、course、score、teacher类。3.新建Service文件夹,建立DBhelper帮助类。(这里直接使用原生数据库连接操作)4.在Controllers层建立StudentsController控制器,在其内部写具体功能实现的方法(crud)。5.demo测试 三、具体搭建HproseServerDemo1.vs2019新建控制台程序(这里就命名为HproseServerDemo)2.搭建hprose环境3.书写服务初始化4.测试Demo4.1类似于前一篇文章的java客户端搭建,新建一个maven项目4.2书写客户端初始化4.3打包丢进jmeter里测试4.4其余几类的代码 四、测试报告1.并发响应情况: 总结


    前言

    在上一篇文章中,我自己学会了如何通过vs中C#类代码搭建hprose服务端—idea中java类代码搭建客户端—打jar包放进jmeter中进行java请求的测试—获取测试数据。 这篇文章将具体搭建我的学生crud测试实例。


    提示:以下是本篇文章正文内容,下面案例可供参考

    一、测试用例报告(WebApi)

    1.测试目的:为确定该测试用例在测试环境下是否能满足用户的并发量以及在相应并发数情况下平台响应情况。利用性能测试工具模拟并发用户对平台进行压力测试,对其处理能力进行性能评估。 2.术语说明: 事务响应时间:处理具体业务时所花费的时间。 测试场景:通过组织若干类型、若干数量的虚拟用户来模拟真实生产环境中的部分压力情况。 最大并发数:系统在此并发数情况下,必然会出现部分用户请求失败。 吞吐量:单位时间内流经被测系统的数据流量,一般单位为b/s,即每秒钟流经的字节数。 3.测试环境 (1)操作系统环境:windows 10; (2)浏览器环境:google浏览器 (3)压力性测试工具:jmeter 处理器:Intel®Core™i7-6700 CPU @3.4GHz 安装内存:16G 系统类型:64位操作系统 4.测试场景说明(测试用例设计) (1)数据库:4个表,每个表存放500条数据。 (2)测试功能用例:基础crud (3)测试方法说明: 登陆验证: 新增: 删除: 修改: 查询: 获取所有:

    二、具体搭建WebApi

    1. vs2019创建新项目—ASP.NET Web应用程序—配置新项目—创建—WebAPI

    2. 新建Modules文件夹,建立student、course、score、teacher类。

    student using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Web; namespace ApiDemo.Models { public class student { public string sno { get; set; } public string sname { get; set; } public string spwd { get; set; } public string sclass { get; set; } public string ssex { get; set; } } } course using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ApiDemo.Models { public class course { public string cno { get; set; } public string cname { get; set; } public string tno { get; set; } } } score using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ApiDemo.Models { public class score { public string sno { get; set; } public string cno { get; set; } public string degree { get; set; } } } teacher using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ApiDemo.Models { public class teacher { public string tno { get; set; } public string tname { get; set; } public string tsex { get; set; } public string prof { get; set; } public string depart { get; set; } } }

    3.新建Service文件夹,建立DBhelper帮助类。(这里直接使用原生数据库连接操作)

    PS:数据库id和pwd可能和我的不一样哈

    using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Runtime.InteropServices; using System.Web; namespace ApiDemo.Service { public class DBHelper { private const string connectionString = "server=.;database=test;Enlist=true;Pooling=true;Max Pool Size=3000;Min Pool Size=0;Connection Lifetime=300;packet size=1000;uid=sa;pwd=123456"; /// <summary> /// 获取连接对象 /// </summary> private static SqlConnection Con { get { var con = new SqlConnection(connectionString); con.Open(); return con; } } /// <summary> /// 获取指令对象 /// </summary> private static SqlCommand Cmd => Con.CreateCommand(); /// <summary> /// 增删改通用语句 /// </summary> /// <param name="sql"></param> /// <returns></returns> public static bool Update(string sql) { var cmd = Cmd; cmd.CommandText = sql; try { // cmd.Connection.Dispose(); return cmd.ExecuteNonQuery() > 0; } catch { cmd.Connection.Dispose(); return false; } } /// <summary> /// 返回第一行第一列的值 /// </summary> /// <param name="sql"></param> /// <returns></returns> public static object SelectForScalar(string sql) { var cmd = Cmd; cmd.CommandText = sql; try { object executeScalar = cmd.ExecuteScalar(); cmd.Connection.Dispose(); return executeScalar; } catch { cmd.Connection.Dispose(); return "wrong"; } } /// <summary> /// 返回datareader /// </summary> /// <param name="sql"></param> /// <returns></returns> public static SqlDataReader SelectForReader(string sql) { var cmd = Cmd; cmd.CommandText = sql; try { return cmd.ExecuteReader(CommandBehavior.CloseConnection); } catch { cmd.Connection.Dispose(); return null; } } } }

    4.在Controllers层建立StudentsController控制器,在其内部写具体功能实现的方法(crud)。

    using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Net; using System.Net.Http; using System.Runtime.InteropServices; using System.Web.Http; using ApiDemo.Models; using ApiDemo.Service; namespace ApiDemo.Controllers { //webapid 控制器 [RoutePrefix("api/student")] public class StudentsController : ApiController { /// <summary> /// 登陆验证 /// </summary> /// <param name="sno"></param> /// <param name="spwd"></param> /// <returns></returns> [HttpGet] [Route("LoginStu")] public bool Login(string sno, string spwd) { string sql = $@"select count(0) from student where sno = '{sno}' and spwd = '{spwd}'"; int selectForScalar = (int)DBHelper.SelectForScalar(sql); return selectForScalar == 1; } /// <summary> /// 新增 /// </summary> /// <param name="stu"></param> /// <returns></returns> [HttpPost] [Route("CreateStu")] public student CreateStudent(student stu) { string sno = stu.sno; string sname = stu.sname; string ssex = stu.ssex; string spwd = stu.spwd; string sclass = stu.sclass; string sql = $@"insert into student values ('{sno}','{sname}','{ssex}','{spwd}','{sclass}')"; DBHelper.Update(sql); return new student() { sno = stu.sno, sname = stu.sname, spwd = stu.spwd, ssex = stu.ssex, sclass = stu.sclass }; } /// <summary> /// 更新 /// </summary> /// <param name="sno"></param> /// <param name="oldspwd"></param> /// <param name="newspwd"></param> /// <returns></returns> [HttpGet] [Route("UpdateStu")] public student UpdateStudent(string sno, string oldspwd, string newspwd) { bool login = Login(sno, oldspwd); if (login) { string sql = $@"update student set spwd='{newspwd}' where sno = '{sno}'"; DBHelper.Update(sql); } else { throw new ArgumentException("您输入的原始账号密码有误"); } return new student() { sno = sno, spwd = newspwd, }; } /// <summary> /// 删除用户 /// </summary> /// <param name="sno"></param> /// <returns></returns> [HttpGet] [Route("DeleteStu")] public bool DeleteStudent(string sno) { string sql = $@"delete from student where sno='{sno}'"; DBHelper.Update(sql); return true; } /// <summary> /// 查询用户 /// </summary> /// <param name="sno"></param> /// <returns></returns> [HttpGet] [Route("GetStu")] public student GetInfoBysno(string sno) { string sql = $@"select * from student where sno = {sno}"; SqlDataReader selectForReader = DBHelper.SelectForReader(sql); student student = new student(); if (selectForReader.Read()) { student.sno = sno; student.sname = selectForReader.GetString(1); student.ssex = selectForReader.GetString(2); student.spwd = selectForReader.GetString(3); student.sclass = selectForReader.GetString(4); } selectForReader.Close(); return student; } /// <summary> /// 查看所有信息 /// </summary> /// <returns></returns> [HttpGet] [Route("GetAllStu")] public List<student> GetAllStu() { string sql = "select * from student s join score sc on sc.sno = s.sno"; SqlDataReader selectForReader = DBHelper.SelectForReader(sql); List<student> students = new List<student>(); if (selectForReader.Read()) { while (selectForReader.Read()) { var student = new student(); student.sno = selectForReader[0].ToString(); student.sname = selectForReader[1].ToString(); student.ssex = selectForReader[2].ToString(); student.spwd = selectForReader[3].ToString(); student.sclass = selectForReader[4].ToString(); students.Add(student); } } selectForReader.Close(); return students; } 搭建好的项目结构如图

    5.demo测试

    运行ApiDemo 使用postman或者jmeter进行测试,这里贪图方便就用postman吧(毕竟轻量化测试工具) bingo~测试成功

    三、具体搭建HproseServerDemo

    1.vs2019新建控制台程序(这里就命名为HproseServerDemo)

    2.搭建hprose环境

    添加引用工具—Nuget包—管理解决方案的Nuget包搜索hprose,全部下载好啦 在添加引用一个名为hprose.dll的dll文件,我也不知道师姐哪里来的。

    3.书写服务初始化

    同上,新建Models文件夹,建立四个类。(可以直接复制粘贴前面的api项目里的,然后包括进项目中)同上,新建DBhelper类,用于简单的和数据库的交互操作。(可以直接复制粘贴前面的api项目里的,然后包括进项目中)新建IStudentsService接口和实现该接口的StudentsService类。(接口提供方法名,类负责实现方法,方法同上)项目结构如图 接口及实现类代码如下 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HproseServerDemo { public interface IStudentsService { bool Login(string sno, string spwd); student CreateStudent(student stu); student UpdateStudent(string sno, string oldspwd, string newspwd); bool DeleteStudent(string sno); student GetInfoBysno(string sno); List<student> GetAllStu(); } } using System; using System.Collections.Generic; using System.Data.SqlClient; namespace HproseServerDemo { public class StudentsService:IStudentsService { /// <summary> /// 登陆验证 /// </summary> /// <param name="sno"></param> /// <param name="spwd"></param> /// <returns></returns> public bool Login(string sno, string spwd) { string sql = $@"select count(0) from student where sno = '{sno}' and spwd = '{spwd}'"; int selectForScalar = (int)DBHelper.SelectForScalar(sql); return selectForScalar == 1; } /// <summary> /// 新增 /// </summary> /// <param name="stu"></param> /// <returns></returns> public student CreateStudent(student stu) { string sno = stu.sno; string sname = stu.sname; string ssex = stu.ssex; string spwd = stu.spwd; string sclass = stu.sclass; string sql = $@"insert into student values ('{sno}','{sname}','{ssex}','{spwd}','{sclass}')"; DBHelper.Update(sql); return new student() { sno = stu.sno, sname = stu.sname, spwd = stu.spwd, ssex = stu.ssex, sclass = stu.sclass }; } /// <summary> /// 更新密码 /// </summary> /// <param name="sno"></param> /// <param name="oldspwd"></param> /// <param name="newspwd"></param> /// <returns></returns> public student UpdateStudent(string sno, string oldspwd, string newspwd) { bool login = Login(sno, oldspwd); if (login) { string sql = $@"update student set spwd='{newspwd}' where sno = '{sno}'"; DBHelper.Update(sql); } else { throw new ArgumentException("您输入的原始账号密码有误"); } return new student() { sno = sno, spwd = newspwd, }; } /// <summary> /// 删除用户 /// </summary> /// <param name="sno"></param> /// <returns></returns> public bool DeleteStudent(string sno) { string sql = $@"delete from student where sno='{sno}'"; DBHelper.Update(sql); return true; } /// <summary> /// 查询用户 /// </summary> /// <param name="sno"></param> /// <returns></returns> public student GetInfoBysno(string sno) { string sql = $@"select * from student where sno = {sno}"; SqlDataReader selectForReader = DBHelper.SelectForReader(sql); student student = new student(); if (selectForReader.Read()) { student.sno = sno; student.sname = selectForReader.GetString(1); student.ssex = selectForReader.GetString(2); student.spwd = selectForReader.GetString(3); student.sclass = selectForReader.GetString(4); } selectForReader.Close(); return student; } /// <summary> /// 查看所有信息 /// </summary> /// <returns></returns> public List<student> GetAllStu() { string sql = "select * from student s join score sc on sc.sno = s.sno"; SqlDataReader selectForReader = DBHelper.SelectForReader(sql); List<student> students = new List<student>(); if (selectForReader.Read()) { while (selectForReader.Read()) { var student = new student(); student.sno = selectForReader[0].ToString(); student.sname = selectForReader[1].ToString(); student.ssex = selectForReader[2].ToString(); student.spwd = selectForReader[3].ToString(); student.sclass = selectForReader[4].ToString(); students.Add(student); } } selectForReader.Close(); return students; }

    4.测试Demo

    在这里,我们有两种选择,1.用vs写c#类客户端调用服务2.用idea写java类客户端调用服务考虑到我们最后需要用jmeter进行java脚本的测试,故选择第二种。

    4.1类似于前一篇文章的java客户端搭建,新建一个maven项目

    预防前一篇文章没有看到的同学,讲一下maven项目的初始化,1)在pom中引入依赖hprose-java,版本为2.0.38 2)从jmeter中引入jar包,分别是ApacheJMeter_core.jar和ApacheJMeter_java.jar

    4.2书写客户端初始化

    为了能够使得java脚本编写的方法返回类型有迹可循,我们需要建立pojo类,即实体类。(这里偷懒了,只建立了student类) package pojo; public class Student { public String sno ; public String sname ; public String spwd ; public String sclass ; public String ssex ; public Student(String sno, String sname, String spwd, String sclass, String ssex) { this.sno = sno; this.sname = sname; this.spwd = spwd; this.sclass = sclass; this.ssex = ssex; } public Student() { } public String getSno() { return sno; } public void setSno(String sno) { this.sno = sno; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSpwd() { return spwd; } public void setSpwd(String spwd) { this.spwd = spwd; } public String getSclass() { return sclass; } public void setSclass(String sclass) { this.sclass = sclass; } public String getSsex() { return ssex; } public void setSsex(String ssex) { this.ssex = ssex; } @Override public String toString() { return "Student{" + "sno='" + sno + '\'' + ", sname='" + sname + '\'' + ", spwd='" + spwd + '\'' + ", sclass='" + sclass + '\'' + ", ssex='" + ssex + '\'' + '}'; } } 新建一个和服务端相同的接口IStudentsService,方法名、参数、返回值都必须相同呢。 package service; import com.sun.org.apache.xpath.internal.operations.Bool; import pojo.Student; import java.util.List; public interface IStudentsService { Boolean Login(String sno, String spwd) ; Student CreateStudent(Student stu) ; Student UpdateStudent(String sno, String oldspwd, String newspwd) ; Boolean DeleteStudent(String sno) ; Student GetInfoBysno(String sno) ; List<Student> GetAllStu() ; } 新建几个类,在类里建立hprose的client并使用接口,调用服务端的方法。(PS:重要的还有类必须继承AbstractJavaSamplerClient静态类,重写其四个方法,每个方法都有很大的作用!)例如类GetInfoBysno代码如下 package service; import hprose.client.HproseHttpClient; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; import pojo.Student; import service.IStudentsService; public class GetInfoBysno extends AbstractJavaSamplerClient { private SampleResult results; private String sno ; public GetInfoBysno() { } /** * 初始化方法,初始化性能测试时的每个线程 * 实际运行时每个线程仅执行一次,在测试方法运行前执行,类似于LoadRunner中的init方法 */ public void setupTest(JavaSamplerContext jsc) { this.results = new SampleResult(); this.sno = jsc.getParameter("sno","") ; if (this.sno!=null && this.sno.length()>0) { this.results.setSamplerData(this.sno); } } /** * 设置传入参数 * 可以设置多个,已设置的参数会显示到Jmeter参数列表中 */ public Arguments getDefaultParameters() { Arguments params = new Arguments(); params.addArgument("sno",""); return params; } /** * 性能测试时的线程运行体 * 测试执行的循环体,根据线程数和循环次数的不同可执行多次,类似于Loadrunner中的Action方法 */ public SampleResult runTest(JavaSamplerContext javaSamplerContext) { // System.out.println("START"); results.sampleStart(); HproseHttpClient client = new HproseHttpClient("http://localhost:2010/"); IStudentsService proxy = client.useService(IStudentsService.class); Student student = proxy.GetInfoBysno(sno); results.sampleEnd(); if (student!=null) { results.setSuccessful(true); } else { results.setSuccessful(false); } String s = student.toString(); // System.out.println(s); // System.out.println("END"); this.results.setResponseData(s,null); return this.results; } /** * 测试结束方法,结束测试中的每个线程 * 实际运行时,每个线程仅执行一次,在测试方法运行结束后执行,类似于Loadrunner中的End方法 */ public void teardownTest(JavaSamplerContext arg0) { } }

    其中:

    setupTest中jsc.getParameter(“sno”,"") 为设置一个名为sno的参数getDefaultParameters中params.addArgument(“sno”,"")为测试时设置的传入参数,给sno赋值runTest中results.sampleStart()为计时器开始;results.sampleEnd()为计时器结束;results.setSuccessful(true)设定为成功的状态;this.results.setResponseData(s,null)设定jmeter中请求数据中可以显示的内容。teardownTest设定线程结束应该执行的代码。其他几类代码文末贴出。 项目结构如下:

    4.3打包丢进jmeter里测试

    idea打包过程前一篇有哈,这里就赘述了打好的jar包丢进jmeter\lib\ext中,重启jmeter测试新建线程—添加java请求、查看结果树、聚合报告选择好java请求里的类,输入特定的参数(此处可以用jmeter的子函数,例如计时器或随机数) 测试 bingo~

    4.4其余几类的代码

    CreateStudent package service; import hprose.client.HproseHttpClient; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; import pojo.Student; public class CreateStudent extends AbstractJavaSamplerClient { private SampleResult results; private String sno ; private String sname ; private String ssex ; private String spwd ; private String sclass ; public CreateStudent() { } /** * 初始化方法,初始化性能测试时的每个线程 * 实际运行时每个线程仅执行一次,在测试方法运行前执行,类似于LoadRunner中的init方法 */ public void setupTest(JavaSamplerContext jsc) { this.results = new SampleResult(); this.sno = jsc.getParameter("sno","") ; this.sname = jsc.getParameter("sname","") ; this.ssex = jsc.getParameter("ssex","") ; this.spwd = jsc.getParameter("spwd","") ; this.sclass = jsc.getParameter("sclass","") ; if (this.sno!=null && this.sno.length()>0) { this.results.setSamplerData(this.sno); } if (this.sname!=null&& this.sname.length()>0) { this.results.setSamplerData(this.sname); } if (this.ssex!=null&& this.ssex.length()>0) { this.results.setSamplerData(this.ssex); } if (this.spwd!=null&& this.spwd.length()>0) { this.results.setSamplerData(this.spwd); } if (this.sclass!=null&& this.sclass.length()>0) { this.results.setSamplerData(this.sclass); } } /** * 设置传入参数 * 可以设置多个,已设置的参数会显示到Jmeter参数列表中 */ public Arguments getDefaultParameters() { Arguments params = new Arguments(); params.addArgument("sno",""); params.addArgument("sname",""); params.addArgument("ssex",""); params.addArgument("spwd",""); params.addArgument("sclass",""); return params; } /** * 性能测试时的线程运行体 * 测试执行的循环体,根据线程数和循环次数的不同可执行多次,类似于Loadrunner中的Action方法 */ public SampleResult runTest(JavaSamplerContext javaSamplerContext) { // System.out.println("START"); results.sampleStart(); HproseHttpClient client = new HproseHttpClient("http://localhost:2010/"); IStudentsService proxy = client.useService(IStudentsService.class); Student student = new Student(); student.sno = this.sno; student.sname = this.sname ; student.ssex = this.ssex ; student.spwd = this.spwd ; student.sclass = this.sclass ; Student student1 = proxy.CreateStudent(student); results.sampleEnd(); if (student1!=null) { results.setSuccessful(true); } else { results.setSuccessful(false); } String s = student1.toString(); // System.out.println(s); // System.out.println("END"); this.results.setResponseData(s,null); return this.results; } /** * 测试结束方法,结束测试中的每个线程 * 实际运行时,每个线程仅执行一次,在测试方法运行结束后执行,类似于Loadrunner中的End方法 */ public void teardownTest(JavaSamplerContext arg0) { } } DeleteStudent package service; import hprose.client.HproseHttpClient; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; import pojo.Student; public class DeleteStudent extends AbstractJavaSamplerClient { private SampleResult results; private String sno ; public DeleteStudent() { } /** * 初始化方法,初始化性能测试时的每个线程 * 实际运行时每个线程仅执行一次,在测试方法运行前执行,类似于LoadRunner中的init方法 */ public void setupTest(JavaSamplerContext jsc) { this.results = new SampleResult(); this.sno = jsc.getParameter("sno","") ; if (this.sno!=null && this.sno.length()>0) { this.results.setSamplerData(this.sno); } } /** * 设置传入参数 * 可以设置多个,已设置的参数会显示到Jmeter参数列表中 */ public Arguments getDefaultParameters() { Arguments params = new Arguments(); params.addArgument("sno",""); return params; } /** * 性能测试时的线程运行体 * 测试执行的循环体,根据线程数和循环次数的不同可执行多次,类似于Loadrunner中的Action方法 */ public SampleResult runTest(JavaSamplerContext javaSamplerContext) { // System.out.println("START"); results.sampleStart(); HproseHttpClient client = new HproseHttpClient("http://localhost:2010/"); IStudentsService proxy = client.useService(IStudentsService.class); Boolean b = proxy.DeleteStudent(sno); results.sampleEnd(); if (b!=null) { results.setSuccessful(true); } else { results.setSuccessful(false); } String s = b.toString(); // System.out.println(s); // System.out.println("END"); this.results.setResponseData(s,null); return this.results; } /** * 测试结束方法,结束测试中的每个线程 * 实际运行时,每个线程仅执行一次,在测试方法运行结束后执行,类似于Loadrunner中的End方法 */ public void teardownTest(JavaSamplerContext arg0) { } } //interface IHello { // String hello(String name); //} //interface Hello { // String sayHello(String input) ; //} // public static void main(String[] args) throws Throwable { // System.out.println("START"); // HproseTcpClient client = new HproseTcpClient("tcp://localhost:4322"); // IHello helloClient = client.useService(IHello.class); // System.out.println(helloClient.hello("World")); // System.out.println("END"); // // } GetAllStu package service; import hprose.client.HproseHttpClient; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; import pojo.Student; import java.util.List; public class GetAllStu extends AbstractJavaSamplerClient { private SampleResult results; public GetAllStu() { } /** * 初始化方法,初始化性能测试时的每个线程 * 实际运行时每个线程仅执行一次,在测试方法运行前执行,类似于LoadRunner中的init方法 */ public void setupTest(JavaSamplerContext jsc) { this.results = new SampleResult(); } /** * 设置传入参数 * 可以设置多个,已设置的参数会显示到Jmeter参数列表中 */ public Arguments getDefaultParameters() { Arguments params = new Arguments(); return params; } /** * 性能测试时的线程运行体 * 测试执行的循环体,根据线程数和循环次数的不同可执行多次,类似于Loadrunner中的Action方法 */ public SampleResult runTest(JavaSamplerContext javaSamplerContext) { // System.out.println("START"); results.sampleStart(); HproseHttpClient client = new HproseHttpClient("http://localhost:2010/"); IStudentsService proxy = client.useService(IStudentsService.class); List<Student> students = proxy.GetAllStu(); results.sampleEnd(); if (students!=null) { results.setSuccessful(true); } else { results.setSuccessful(false); } String s ="" ; for (int i = 0 ; i <students.size();i++) { // System.out.println(students.get(i)); s+=students.get(i).toString(); } this.results.setResponseData(s,null); // System.out.println("END"); return this.results; } /** * 测试结束方法,结束测试中的每个线程 * 实际运行时,每个线程仅执行一次,在测试方法运行结束后执行,类似于Loadrunner中的End方法 */ public void teardownTest(JavaSamplerContext arg0) { } } Login package service; import com.sun.org.apache.xpath.internal.operations.Bool; import hprose.client.HproseHttpClient; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; import pojo.Student; public class Login extends AbstractJavaSamplerClient { private SampleResult results; private String inNum; private String resultNum; private String sno ; private String spwd ; public Login() { } /** * 初始化方法,初始化性能测试时的每个线程 * 实际运行时每个线程仅执行一次,在测试方法运行前执行,类似于LoadRunner中的init方法 */ public void setupTest(JavaSamplerContext jsc) { this.results = new SampleResult(); this.sno = jsc.getParameter("sno","") ; this.spwd = jsc.getParameter("spwd","") ; if (this.sno!=null && this.sno.length()>0) { this.results.setSamplerData(this.sno); } if (this.spwd!=null&& this.spwd.length()>0) { this.results.setSamplerData(this.spwd); } } /** * 设置传入参数 * 可以设置多个,已设置的参数会显示到Jmeter参数列表中 */ public Arguments getDefaultParameters() { Arguments params = new Arguments(); params.addArgument("sno",""); params.addArgument("spwd",""); return params; } /** * 性能测试时的线程运行体 * 测试执行的循环体,根据线程数和循环次数的不同可执行多次,类似于Loadrunner中的Action方法 */ public SampleResult runTest(JavaSamplerContext javaSamplerContext) { // System.out.println("START"); results.sampleStart(); HproseHttpClient client = new HproseHttpClient("http://localhost:2010/"); IStudentsService proxy = client.useService(IStudentsService.class); Boolean login = proxy.Login(sno, spwd); results.sampleEnd(); // if (login) // { results.setSuccessful(true); // } // else // { // results.setSuccessful(false); // } String s = login.toString(); // System.out.println(s); // System.out.println("END"); this.results.setResponseData(s,null); return this.results; } /** * 测试结束方法,结束测试中的每个线程 * 实际运行时,每个线程仅执行一次,在测试方法运行结束后执行,类似于Loadrunner中的End方法 */ public void teardownTest(JavaSamplerContext arg0) { } } UpdateStudent package service; import hprose.client.HproseHttpClient; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; import pojo.Student; public class UpdateStudent extends AbstractJavaSamplerClient { private SampleResult results; private String sno ; private String oldspwd ; private String newspwd ; public UpdateStudent() { } /** * 初始化方法,初始化性能测试时的每个线程 * 实际运行时每个线程仅执行一次,在测试方法运行前执行,类似于LoadRunner中的init方法 */ public void setupTest(JavaSamplerContext jsc) { this.results = new SampleResult(); this.sno = jsc.getParameter("sno","") ; this.oldspwd = jsc.getParameter("oldspwd","") ; this.newspwd = jsc.getParameter("newspwd","") ; if (this.newspwd != null && this.newspwd.length() > 0) { this.results.setSamplerData(this.newspwd); } if (this.oldspwd != null && this.oldspwd.length() > 0) { this.results.setSamplerData(this.oldspwd); } if (this.sno!=null && this.sno.length()>0) { this.results.setSamplerData(this.sno); } } /** * 设置传入参数 * 可以设置多个,已设置的参数会显示到Jmeter参数列表中 */ public Arguments getDefaultParameters() { Arguments params = new Arguments(); params.addArgument("sno",""); params.addArgument("oldspwd",""); params.addArgument("newspwd",""); return params; } /** * 性能测试时的线程运行体 * 测试执行的循环体,根据线程数和循环次数的不同可执行多次,类似于Loadrunner中的Action方法 */ public SampleResult runTest(JavaSamplerContext javaSamplerContext) { // System.out.println("START"); results.sampleStart(); HproseHttpClient client = new HproseHttpClient("http://localhost:2010/"); IStudentsService proxy = client.useService(IStudentsService.class); Student student = proxy.UpdateStudent(sno,oldspwd,newspwd); results.sampleEnd(); if (student!=null) { results.setSuccessful(true); } else { results.setSuccessful(false); } String s = student.toString(); // System.out.println(s); // System.out.println("END"); this.results.setResponseData(s,null); return this.results; } /** * 测试结束方法,结束测试中的每个线程 * 实际运行时,每个线程仅执行一次,在测试方法运行结束后执行,类似于Loadrunner中的End方法 */ public void teardownTest(JavaSamplerContext arg0) { } }

    四、测试报告

    1.并发响应情况:

    并发数500,并发时间10 Api Demo Hprose Demo 结论:样本均值、最大最小值、标准偏差上hprose显著小于webapi,其他指标相近。Active Threads Over Time: Api Demo Hprose Demo 结论:测试开始时api每秒活跃线程数多,后与hprose持平。Bytes Throughput Over Time: Api Demo: Hprose Demo: 结论:webapi即时吞吐量大于hprose,稳定性差于hprose。Connect Times Over Time: Api Demo: Hprose Demo: 结论:webapi即时连接次数大于hprose,且波动较大。Hits per Second: Api Demo : Hprose Demo: Response Latencies Over Time: Api Demo : Hprose Demo : 结论:webapi有响应延时,随时间变化降低,hprose几乎无响应延时。Response Times Over Time: Api Demo: Response Times vs Threads: Api Demo: Hprose Demo: Transaction Throughput vs Threads: Api Demo: Hprose Demo : Transactions per Second: Api Demo : Hprose Demo : 并发数500,并发时间5 Api Demo Hprose Demo Active Threads Over Time: Api Demo Hprose Demo Bytes Throughput Over Time: Api Demo: Hprose Demo: Connect Times Over Time: Api Demo: Hprose Demo: Hits per Second: Api Demo : Hprose Demo: Response Latencies Over Time: Api Demo : Hprose Demo : Response Times Over Time: Api Demo: Hprose Demo : Response Times vs Threads: Api Demo: Hprose Demo: Transaction Throughput vs Threads: Api Demo: Hprose Demo : Transactions per Second: Api Demo : Hprose Demo : 并发响应情况:并发数500,并发时间3 Api Demo Hprose Demo Active Threads Over Time: Api Demo Hprose Demo Bytes Throughput Over Time: Api Demo: Hprose Demo: Connect Times Over Time: Api Demo: Hprose Demo: Hits per Second: Api Demo : Hprose Demo: Response Latencies Over Time: Api Demo :

    Hprose Demo :

    Response Times Over Time: Api Demo: Hprose Demo : Response Times vs Threads: Api Demo: Hprose Demo: Transaction Throughput vs Threads: Api Demo: Hprose Demo : Transactions per Second: Api Demo : Hprose Demo :

    总结

    提示:这里对文章进行总结: 每次测试可能有些许偏差,数据库搭建的部分还没有给出来,jmeter插件及随机函数、计数器、简单控制器都没有给出具体的步骤。

    Processed: 0.027, SQL: 8