CallmeJack 发表于 2018-2-8 13:33:13

Jmeter 二次封装

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

原理

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

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

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

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

      // Initialize JMeter SaveService
      SaveService.loadProperties();

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

      // Loop Controller
      LoopController loopController = new LoopController();
      loopController.addTestElement(javaSample);
      loopController.setLoops(caseInfo.getLoop());
      loopController.setFirst(true);
      loopController.initialize();

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

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

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

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

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


      // Store execution results
      MyResultCollector requestCollector = new MyResultCollector();
      requestCollector.setFilename(Constant.logFilePath()+"result.log");
      testPlanTree.add(testPlanTree.getArray(), requestCollector);
      // Run Test Plan
      StandardJMeterEngine jmeterEngine = new StandardJMeterEngine();
      jmeterEngine.configure(testPlanTree);
      jmeterEngine.run();

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

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

    @Override
    public Arguments getDefaultParameters() {
      System.out.println("===========init parameters ========");
      Arguments arg = new Arguments();
      return arg;
    }
    public SampleResult runTest(JavaSamplerContext paramJavaSamplerContext) {
      SampleResult sr = new SampleResult();
      sr.sampleStart();
      //System.out.println(123);

            //此处是调用逻辑

      sr.setSuccessful(true);
      sr.sampleEnd();
      return sr;
    }

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

    private static final long serialVersionUID = -8648350950445938218L;

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

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

    @Override
    public void sampleOccurred(SampleEvent e) {
      super.sampleOccurred(e);
      SampleResult r = e.getResult();
      int threadGroupName = Integer.parseInt(e.getThreadGroup());
      if(threadResultsMap.containsKey(threadGroupName)){
            List<SampleResult> sampleResults = threadResultsMap.get(threadGroupName);
            sampleResults.add(r);
      }else{
            List<SampleResult> sampleResults = new ArrayList<SampleResult>();
            sampleResults.add(r);
            threadResultsMap.put(threadGroupName, sampleResults);
      }
    }

    public Map<Integer,List<SampleResult>> getSampleResults() {
      return threadResultsMap;
    }
}首先是扩展Jmeter自己的结果收集器。

CallmeJack 发表于 2018-2-8 13:34:57

public static void createChart_Jmeter(int type, Map<Integer, List<SampleResult>> sampleResultsMap,String className) throws IOException {
      ArrayList<double[]> tpsSeries = new ArrayList<double[]>();
      ArrayList<double[]> averageSeries = new ArrayList<double[]>();
      ArrayList<double[]> minSeries = new ArrayList<double[]>();
      ArrayList<double[]> maxSeries = new ArrayList<double[]>();
      ArrayList<double[]> time60 = new ArrayList<double[]>();
      ArrayList<double[]> time90 = new ArrayList<double[]>();
      ArrayList<double[]> time95 = new ArrayList<double[]>();

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

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

            double tps = aggregated.getTps();
            double averageTime = aggregated.getAverageTime();
            long maxTime = aggregated.getMaxTime();
            long minTime = aggregated.getMinTime();
            long per60Time = aggregated.getPercentTime(0.6);
            long per90Time = aggregated.getPercentTime(0.9);
            long per95Time = aggregated.getPercentTime(0.95);
            double errorRate = aggregated.getErrorRate();

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

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


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

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


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

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

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

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

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

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

效果图

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


以及


梦想家 发表于 2018-2-28 17:13:16

支持分享

Miss_love 发表于 2020-12-30 08:35:55

感谢分享
页: [1]
查看完整版本: Jmeter 二次封装