51Testing软件测试论坛

标题: Jmeter 二次封装 [打印本页]

作者: CallmeJack    时间: 2018-2-8 13:33
标题: Jmeter 二次封装
     之前在老东家做性能测试的时候,一开始是使用LR的(服务端测试)。在LR里写vuser scripts 去进行
RPC协议的接口性能测试。但是后来觉得LR实在太重了,一般的机器消受不了。而且无法自动化的驱动测
试。所以后来引入了Jmeter。但是Jmeter也有它的缺点, 尤其是最后结果统计中那蛋疼的报表简直low到爆。
所以临走前搞了一次二次封装。增强jmeter的特性,可惜只做了比较少的一部分我就离职了。 这里我写出
来就当抛砖引玉了吧。

原理

其实原理十分简单,我们一般就是用Jmeter的GUI来完成工作么。现在我们不用GUI了,我们直接引用
jmeter的核心lib,直接调用jmeter的API来执行我们的操作。你需要引入maven的依赖。如下
  1. <dependency>
  2.         <groupId>com.lazerycode.jmeter</groupId>
  3.         <artifactId>jmeter-maven-plugin</artifactId>
  4.         <version>1.10.1</version>
  5.         <exclusions>
  6.             <exclusion>
  7.                 <artifactId>ApacheJMeter_config</artifactId>
  8.                 <groupId>org.apache.jmeter</groupId>
  9.             </exclusion>
  10.         </exclusions>
  11.     </dependency>
复制代码
然后运行的时候,你还是需要装一个Jmeter的,这个我没绕过去。需要在代码中设置jmter的home。
以及一些其他的配置信息
  1. JMeterUtils.setJMeterHome(Constant.getJmeterHome());
  2.         JMeterUtils.loadJMeterProperties(Constant.getJmeterHome()
  3.                 + "/bin/jmeter.properties");
  4.         JMeterUtils.setProperty("jmeter.save.saveservice.output_format", "xml");
  5.         File log = new File(Constant.logFilePath() + "perfTest.log");
  6. private static String jmeterHome = "E:\\apache-jmeter-2.13";//jmeter的home目录
复制代码
代码部分

现在我们来看看核心代码部分吧
  1. Logger.info("测试方法: "+caseInfo.getName());
  2.         Logger.info("起始并发数: "+caseInfo.getBeginThread());
  3.         Logger.info("递增并发数: "+caseInfo.getAddThread());
  4.         Logger.info("结束并发数: "+caseInfo.getEndThread());
  5.         Logger.info("每个线程的循环次数: "+caseInfo.getLoop());
  6.         //Logger.html_link("1111", "222");

  7.         JMeterUtils.setJMeterHome(Constant.getJmeterHome());
  8.         JMeterUtils.loadJMeterProperties(Constant.getJmeterHome()
  9.                 + "/bin/jmeter.properties");
  10.         JMeterUtils.setProperty("jmeter.save.saveservice.output_format", "xml");
  11.         File log = new File(Constant.logFilePath() + "perfTest.log");

  12.         JMeterUtils.setProperty(LoggingManager.LOG_FILE, log.getAbsolutePath());
  13.         JMeterUtils.initLogging();// you can comment this line out to see extra
  14.                                     // log messages of i.e. DEBUG level
  15.         JMeterUtils.initLocale();

  16.         // Initialize JMeter SaveService
  17.         SaveService.loadProperties();

  18.         JavaSampler javaSample = new JavaSampler();
  19.         javaSample.setClassname(caseInfo.getName());

  20.         // Loop Controller
  21.         LoopController loopController = new LoopController();
  22.         loopController.addTestElement(javaSample);
  23.         loopController.setLoops(caseInfo.getLoop());
  24.         loopController.setFirst(true);
  25.         loopController.initialize();

  26.         // Thread Group
  27.         List<ThreadGroup> threadGrouplist = this.setupThreadGroup(caseInfo, loopController);

  28.         // JMeter Test Plan, basic all u JOrphan HashTree
  29.         TestPlan testPlan = new TestPlan("My Test Plan");
  30.         testPlan.setSerialized(true);

  31.         // JMeter Test Plan, basic all u JOrphan HashTree
  32.         HashTree testPlanTree = new HashTree();

  33.         // Construct Test Plan from previously initialized elements
  34.         testPlanTree.add("TestPlan", testPlan);
  35.         testPlanTree.add("LoopController", loopController);
  36.         testPlanTree.add("JavaSampler", javaSample);

  37.         int count = 1000;
  38.         for(ThreadGroup threadGroup:threadGrouplist){
  39.             testPlanTree.add("ThreadGroup"+count,threadGroup);
  40.             count--;
  41.         }


  42.         // Store execution results
  43.         MyResultCollector requestCollector = new MyResultCollector();
  44.         requestCollector.setFilename(Constant.logFilePath()+"result.log");
  45.         testPlanTree.add(testPlanTree.getArray()[0], requestCollector);
  46.         // Run Test Plan
  47.         StandardJMeterEngine jmeterEngine = new StandardJMeterEngine();
  48.         jmeterEngine.configure(testPlanTree);
  49.         jmeterEngine.run();

  50.         Map<Integer,List<SampleResult>> sampleResultsMap = requestCollector.getSampleResults();
  51.         for (Object key : sampleResultsMap.keySet()) {
  52.             List<SampleResult> list = sampleResultsMap.get(key);
  53.             /*for(SampleResult s : list){
  54.                 System.out.println(s.getTime()+"fffff");
  55.             }*/
  56.             System.out.println(list.size()+"kkkkkkkkkkk");

  57.         }
  58.         PerfChartHelper.createChart_Jmeter(1, sampleResultsMap,caseInfo.getName());
复制代码
可以看到,我们在代码中设置循环,设置线程组,测试计划等等,熟悉jmeter的同学一定熟悉这些
概念。其中我们测试的java的接口么。所以用的是javasimpler
  1. JavaSampler javaSample = new JavaSampler();
  2.         javaSample.setClassname(caseInfo.getName());
复制代码
其中需要给sample传一个测试类(如果是http接口,其实就不需要了)。下面我们看看这个测试类是
怎么定义的。
  1. public class TestP extends AbstractJavaSamplerClient {
  2.     private static AtomicInteger temp = new AtomicInteger();

  3.     @Override
  4.     public Arguments getDefaultParameters() {
  5.         System.out.println("===========init parameters ========");
  6.         Arguments arg = new Arguments();
  7.         return arg;
  8.     }
  9.     public SampleResult runTest(JavaSamplerContext paramJavaSamplerContext) {
  10.         SampleResult sr = new SampleResult();
  11.         sr.sampleStart();
  12.         //System.out.println(123);

  13.             //此处是调用逻辑

  14.         sr.setSuccessful(true);
  15.         sr.sampleEnd();
  16.         return sr;
  17.     }

  18. }
复制代码
可以看到我们只要继承jmeter定义的类AbstractJavaSamplerClient 就可以了。 jmeter也是有集合点这个概
念的。即便没有,现在你都能调用它的API了,你自己写一个实现嵌入进去也行。然后我们看结果收集,
这里我是自己进行了计算。
  1. public class MyResultCollector extends ResultCollector {

  2.     private static final long serialVersionUID = -8648350950445938218L;

  3.     //private List<SampleResult> sampleResults;
  4.     private Map<Integer,List<SampleResult>> threadResultsMap;

  5.     public MyResultCollector() {
  6.         threadResultsMap = new HashMap<Integer,List<SampleResult>>();
  7.     }

  8.     @Override
  9.     public void sampleOccurred(SampleEvent e) {
  10.         super.sampleOccurred(e);
  11.         SampleResult r = e.getResult();
  12.         int threadGroupName = Integer.parseInt(e.getThreadGroup());
  13.         if(threadResultsMap.containsKey(threadGroupName)){
  14.             List<SampleResult> sampleResults = threadResultsMap.get(threadGroupName);
  15.             sampleResults.add(r);
  16.         }else{
  17.             List<SampleResult> sampleResults = new ArrayList<SampleResult>();
  18.             sampleResults.add(r);
  19.             threadResultsMap.put(threadGroupName, sampleResults);
  20.         }
  21.     }

  22.     public Map<Integer,List<SampleResult>> getSampleResults() {
  23.         return threadResultsMap;
  24.     }
  25. }
复制代码
首先是扩展Jmeter自己的结果收集器。


作者: CallmeJack    时间: 2018-2-8 13:34
  1. public static void createChart_Jmeter(int type, Map<Integer, List<SampleResult>> sampleResultsMap,String className) throws IOException {
  2.         ArrayList<double[]> tpsSeries = new ArrayList<double[]>();
  3.         ArrayList<double[]> averageSeries = new ArrayList<double[]>();
  4.         ArrayList<double[]> minSeries = new ArrayList<double[]>();
  5.         ArrayList<double[]> maxSeries = new ArrayList<double[]>();
  6.         ArrayList<double[]> time60 = new ArrayList<double[]>();
  7.         ArrayList<double[]> time90 = new ArrayList<double[]>();
  8.         ArrayList<double[]> time95 = new ArrayList<double[]>();

  9.         //用treeSet排序
  10.         TreeSet<Integer> tree = new TreeSet<Integer>();
  11.         for(int thread : sampleResultsMap.keySet()) {
  12.             tree.add(thread);
  13.         }

  14.         // 遍历每一个threadgroup的结果集,算出tps,平均相应时间并画图
  15.         for(int thread : tree) {
  16.             AggregatedParser aggregated = new AggregatedParser(sampleResultsMap.get(thread));

  17.             double tps = aggregated.getTps();
  18.             double averageTime = aggregated.getAverageTime();
  19.             long maxTime = aggregated.getMaxTime();
  20.             long minTime = aggregated.getMinTime();
  21.             long per60Time = aggregated.getPercentTime(0.6);
  22.             long per90Time = aggregated.getPercentTime(0.9);
  23.             long per95Time = aggregated.getPercentTime(0.95);
  24.             double errorRate = aggregated.getErrorRate();

  25.             tpsSeries.add(new double[]{thread, tps});
  26.             averageSeries.add(new double[]{thread, averageTime});
  27.             maxSeries.add(new double[]{thread, maxTime});
  28.             minSeries.add(new double[]{thread, minTime});
  29.             time60.add(new double[]{thread, per60Time});
  30.             time90.add(new double[]{thread, per90Time});
  31.             time95.add(new double[]{thread, per95Time});
  32.             Logger.info("在"+thread+"并发下错误率:"+errorRate);
  33.         }

  34.         Map<String, ArrayList<double[]>> timeMap = new HashMap<String, ArrayList<double[]>>();
  35.         timeMap.put("average time", averageSeries);
  36.         timeMap.put("min time", minSeries);
  37.         timeMap.put("max time", maxSeries);
  38.         timeMap.put("60% time", time60);
  39.         timeMap.put("90% time", time90);
  40.         timeMap.put("95% time", time95);


  41.         JfreeChart tpsChart = new JfreeChart("thread_tps", "thread", "tps");
  42.         tpsChart.addXYSeries("thread_tps_report", tpsSeries);
  43.         File folder = new File(Constant.imagePath()+className);
  44.         folder.mkdirs();
  45.         File imgTps = new File(Constant.imagePath()+className, "_tps.png");
  46.         tpsChart.createJfreeChartImage(imgTps, 800, 600);

  47.         //在html report中增加图片链接
  48.         //Logger.html_img(Constant.imagePath()+className+"/_tps.png");
  49.         //echarts_Qps(tpsSeries);


  50.         JfreeChart avgTimeChart = new JfreeChart("avgTime", "thread", "avg_time");
  51.         avgTimeChart.addXYSeries("thread_avgTime", averageSeries);
  52.         File imgAvgTime = new File(Constant.imagePath()+className, "_avg.png");
  53.         avgTimeChart.createJfreeChartImage(imgAvgTime, 800, 600);

  54.         //在html report中增加图片链接
  55.         //Logger.html_img(Constant.imagePath()+className+"/_avg.png");

  56.         //使用echart在html页面中花出tps和平均响应时间的图
  57.         Integer id = tempCount.getAndAdd(1);
  58.         Logger.html("<div id=\"chart_qps"+id+"\" style=\"width:800px;height:600px;\"></div>");
  59.         Logger.html("<div id=\"chart_time"+id+"\" style=\"width:800px;height:600px;\"></div>");
  60.         Logger.html("<script>");
  61.         echarts_Qps(tpsSeries,id);
  62.         echarts_Time(timeMap,id);
  63.         Logger.html("</script>");
复制代码
然后增加结果收集算法。并画出图来。我分别用jfreechart和echart画图。

然后我们一下case控制
  1. <suite name="rock" >
  2.     <paras>
  3.         <para host_ip="10.9.20.171"></para>
  4.     </paras>
  5.     <cases>
  6.         <case name="performance.TestP" run="false">
  7.             <perf begin_thread="100" end_thread="200" add_thread="20" loop="10"/>
  8.             <para desc="根据订单ID查询订单"></para>
  9.         </case>

  10.         <case name="performance.TestP" run="false">
  11.             <perf begin_thread="100" end_thread="200" add_thread="20" loop="10"/>
  12.             <para desc="根据订单ID查询订单"></para>
  13.         </case>

  14.     </cases>
  15. </suite>
复制代码
case里有我们想要控制的信息,例如起始的并发数,每次递增的并发数。结束的并发数等等。
其中name就是我们的测试类的全路径(我这里都是我自己写的demo类)。

效果图

现在我们来看看效果图吧,就是我们最后的结果

[attach]110750[/attach]
以及
[attach]110751[/attach]


作者: 梦想家    时间: 2018-2-28 17:13
支持分享
作者: Miss_love    时间: 2020-12-30 08:35
感谢分享




欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2