性能|性能工具之 Jmeter 通过 SpringBoot 工程启动


文章目录

    • 背景
    • 前置条件
    • 页面设计
    • 运行效果
    • 参考代码
      • 前端代码
      • 服务端接口
      • interface层代码
      • 接口实现层
      • 工具类
    • jmeter脚本
    • 小结

背景 Jmeter 平时性能测试工作一般都是通过命令行在 Linux 下执行,为了锻炼自己代码与逻辑能力,想 Jmeter 是否可以通过 springboot 工程启动,周末在家尝试写一写,一写原来需要处理很多事情,才可以启动起来,起来还是有很问题需要处理,下面是相应的代码,其实网上也有,但关键的是自己有意识收集知识,到用的时候能拿来改一改就用。
前置条件 需要在 linux 中配置 Jmeter,并且配置 Java 环境变量:
## 编辑 vi ~/.bash_profile# jmeter:路径根据自己事情情况修改 JMETER_HOME=/root/tools/apache-jmeter-5.1.1 PATH=$PATH:$HOME/bin:$JMETER_HOME/bin: export PATH## 执行生效 source ~/.bash_profile

页面设计 性能|性能工具之 Jmeter 通过 SpringBoot 工程启动
文章图片

运行效果 点击上传脚本,弹出对话框,点击上传,后台日志显示上传成功。
【性能|性能工具之 Jmeter 通过 SpringBoot 工程启动】性能|性能工具之 Jmeter 通过 SpringBoot 工程启动
文章图片

点击启动,并且读取启动日志。
性能|性能工具之 Jmeter 通过 SpringBoot 工程启动
文章图片

点击停止。
性能|性能工具之 Jmeter 通过 SpringBoot 工程启动
文章图片

示意图说明:
通过访问 -> 调用 JAVA 代码-> 启动 shell命令-> 启动 Jmeter -> 获取启动日志
性能|性能工具之 Jmeter 通过 SpringBoot 工程启动
文章图片

参考代码 前端代码
以下参考代码:
Jmeter启动> .bd-placeholder-img { font-size: 1.125rem; text-anchor: middle; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }@media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; } }
性能|性能工具之 Jmeter 通过 SpringBoot 工程启动
文章图片
The Apache JMeter? application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance. It was originally designed for testing Web Applications but has since expanded to other test functions.
上传脚本
上传脚本
上传参数文件
运行
jmx file
并发数
并发数
运行 停止 查看信息
type="text/javascript" th:src="https://www.it610.com/article/@{/webjars/jquery/3.3.1/jquery.js}"> th:src="https://www.it610.com/article/@{/asserts/js/layer/layer.js}"> >window.jQuery || document.write('<\/script>') src="https://www.it610.com/docs/4.3/dist/js/bootstrap.bundle.js" th:src="https://www.it610.com/article/@{/webjars/bootstrap/4.3.1/js/bootstrap.bundle.js}"> src="https://v4.bootcss.com/docs/4.3/examples/checkout/form-validation.js"> > //上传脚本 function submitupload() { var type = "file"; //后台接收时需要的参数名称,自定义即可 var id = "jmeterId"; //即input的id,用来寻找值 var formData = https://www.it610.com/article/new FormData(); var jmeterId = $("#jmeterId").val(); if (jmeterId == "") { layer.msg("Jmeter文件不能为空,请输入", {time: 2000, icon: 5, shift: 6}, function () { }); return; } formData.append(type, $("#" + id)[0].files[0]); $.ajax({ type: "POST", url: '/jmeter/upload', data: formData, processData: false, contentType: false, success: function (data) { if (data.code == 100) { layer.msg("用户信息保存成功", {time: 1000, icon: 6}, function () { // console.log("相应结果:" + data.extend.file); //通过返回结果进行赋值 $("#jmeterName").val(data.extend.file); // window.location.href = "https://www.it610.com/jmeterIndex"; }); } else { layer.msg("信息保存失败,请重新操作" + data.err, {time: 2000, icon: 5, shift: 6}, function () {}); } } }); }//上传参数 function submitParm() { var type = "file"; //后台接收时需要的参数名称,自定义即可 var id = "jmeterParam"; //即input的id,用来寻找值 var formData = https://www.it610.com/article/new FormData(); var jmeterPara = $("#jmeterParam").val(); if (jmeterPara == "") { layer.msg("Jmeter文件不能为空,请输入", {time: 2000, icon: 5, shift: 6}, function () { }); return; } formData.append(type, $("#" + id)[0].files[0]); $.ajax({ type: "POST", url: '/jmeter/Paramupload', data: formData, processData: false, contentType: false, success: function (data) { if (data.code == 100) { layer.msg("参数文件保存成功", {time: 1000, icon: 6}, function () { }); } else { layer.msg("信息保存失败,请重新操作" + data.err, {time: 2000, icon: 5, shift: 6}, function () {}); } } }); }//运行 function JmeterRun() { let JmeterName = $("#jmeterName").val(); let number = $("#numberName").val(); let duration = $("#duration").val(); console.log(JmeterName); console.log(number); $.ajax({ type: "POST", url: '/jmeter/JmeterRun', data: { "jmeterName": JmeterName, "numberName": number, "duration": duration }, success: function (result) { if (result.code == 100) { layer.msg("启动成功成功", {time: 1000, icon: 6}, function () { }); } else { layer.msg("启动失败,请重新操作", {time: 2000, icon: 5, shift: 6}, function () {}); } } }) }//停止 function Jmeterstop() { $.ajax({ type: "Get", url: '/jmeter/JmeterStop', processData: false, contentType: false, success: function (result) { if (result.code==100) { layer.msg("停止成功", {time: 1000, icon: 6}, function () { }); } else { layer.msg("停止失败,请重新操作", {time: 2000, icon: 5, shift: 6}, function () {}); } } })}//查看日志 function JmeterInfo() { $.ajax({ type: "Get", url: '/jmeter/Jmeterinfo', processData: false, contentType: false, success: function (result) { if (result.code == 100) { layer.msg("启动成功成功", {time: 1000, icon: 6}, function () { $("#JmeterMsg").val(data.extend.infopage); }); } else { layer.msg("启动失败,请重新操作", {time: 2000, icon: 5, shift: 6}, function () {}); } } }) }
服务端接口
参考代码如下:
package com.sevendays.controller; import com.sevendays.pojo.Msg; import com.sevendays.service.JmerterScriptService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; /** * @author 7d * @Title: JmeterController * @Description: Jmeter启动页面 * @date 2019/11/17 / 10:32 */@Controller @RequestMapping("/jmeter") public class JmeterController { private static final Logger logger = LoggerFactory.getLogger(JmeterController.class); @Autowired JmerterScriptService jmerterScriptService; @GetMapping("/jmeterIndex") public String jmeterIndex() { return "jmeter/jmterIndex"; }/** * 上传脚本 * * @param file * @return */ @PostMapping("/upload") @ResponseBody public Msg upload(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return Msg.fail().add("err", "上传失败"); } String fileName = file.getOriginalFilename(); logger.info("路径" + fileName); String filePath = "/home/7d/"; //String filePath = "E:\\test\\7d\\data\\"; if (!fileName.endsWith(".jmx")) { return Msg.fail().add("err", "脚本上传失败"); } File dest = new File(filePath + fileName); String jmxName = fileName.substring(0, fileName.lastIndexOf(".")); try { file.transferTo(dest); logger.info("上传成功:" + jmxName); return Msg.success().add("file", jmxName); } catch (IOException e) { logger.error(e.toString(), e); } return Msg.fail(); }/** * 上传参数文件 * * @param file * @return */ @PostMapping("/Paramupload") @ResponseBody public Msg uploadParam(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return Msg.fail().add("err", "上传失败"); } String fileName = file.getOriginalFilename(); logger.info("路径" + fileName); String filePath = "/home/7d"; //String filePath = "E:\\test\\7d\\data\\"; File dest = new File(filePath + fileName); String jmxName = fileName.substring(0, fileName.lastIndexOf(".")); try { file.transferTo(dest); logger.info("上传成功:" + jmxName); return Msg.success().add("file", jmxName); } catch (IOException e) { logger.error(e.toString(), e); } return Msg.fail(); }/** * 运行脚本 * * @return */ @PostMapping("/JmeterRun") @ResponseBody public Msg run(@RequestParam("jmeterName") String jmeterName, @RequestParam("numberName") String numberName, @RequestParam("duration") String duration) { logger.info(jmeterName); if (!jmeterName.isEmpty() && !numberName.isEmpty()) { jmerterScriptService.runCommand(jmeterName.trim(), numberName.trim(), duration); return Msg.success(); } else { return Msg.fail(); } }/** * 停止脚本 * * @return */ @GetMapping("/JmeterStop") @ResponseBody public Msg stop() { jmerterScriptService.stopCommand(); return Msg.success(); }/** * 查看日志 * * @return */ @GetMapping("/Jmeterinfo") @ResponseBody public Msg info() { String info = jmerterScriptService.selectInfo(); return Msg.success().add("infopage", info); } }

interface层代码
package com.sevendays.service; /** * @author 7d * @Title: JmerterScriptService * @Description: Jmeterj脚本处理 * @date 2019/11/17 / 18:06 */public interface JmerterScriptService {/** * 执行命令 * @param cmd */ void execCommand(String cmd); /** * 运行 * @param script 脚本 * @param num数量 * @param seconds 执行时间 */ void runCommand(String script, String num,String seconds); /** * 停止 */ void stopCommand(); /** * 获取日志 * @return */ String selectInfo(); }

接口实现层
package com.sevendays.service.impl; import com.sevendays.controller.JmeterController; import com.sevendays.service.JmerterScriptService; import com.sevendays.utils.LogSvrReadInput; import com.sevendays.utils.execCmd; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Date; /** * @author 7d * @Title: JmerterScriptServiceImpl * @Description: 执行命令 * @date 2019/11/17 / 18:49 */@Service public class JmerterScriptServiceImpl implements JmerterScriptService {private static final Logger logger = LoggerFactory.getLogger(JmerterScriptServiceImpl.class); @Override public void execCommand(String cmd) { try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec(cmd, null, null); InputStream stderr = proc.getInputStream(); InputStreamReader isr = new InputStreamReader(stderr, "GBK"); BufferedReader br = new BufferedReader(isr); String line = ""; while ((line = br.readLine()) != null) { logger.info(line); } } catch (Exception e) { e.printStackTrace(); }}@Override public void runCommand(String script, String num, String seconds) {//执行次数 String numThread = "#numThread"; //执行时间 String time = "#timeDuration"; String bak = "cp /home/7d/" + script + ".jmx /home/7d/" + script + "bak.jmx"; String old = "/home/7d/" + script + ".jmx"; execCmd.execCmd(bak); logger.info("路径:{}", old); //替换执行数量 execCmd.replacTextContent(old, "#numThread", num); //替换执行时间 execCmd.replacTextContent(old, "#timeDuration", seconds); String runcmd = "nohup jmeter -n -t /home/7d/#scriptName.jmx -l /home/7d/#scriptName.jtl -j /home/7d/jmeter.log > /home/7d/jmeterlog.log&".replaceAll("#scriptName", script); logger.info("运行命令{}", runcmd); execCmd.execCmd(runcmd); }@Override public void stopCommand() { String stoprunm = "/root/tools/apache-jmeter-5.1.1/bin/shutdown.sh"; execCmd.execCmd(stoprunm); }@Override public String selectInfo() { String tail = "tail -f /home/7d/jmeterlog.log"; File file = new File("/home/7d/jmeterlog.log"); String s = LogSvrReadInput.realtimeShowLog(file); logger.info("输出日志:--》{}",s); return s; } }

工具类
package com.sevendays.utils; import com.sevendays.service.impl.JmerterScriptServiceImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; /** * @author 7d * @Title: execCmd * @Description: 直接执行命令 * @date 2019/11/17 / 19:17 */public class execCmd {private static final Logger logger = LoggerFactory.getLogger(execCmd.class); public execCmd() { }/** * 直接执行命令 * * @param cmd */ public static void execCmd(String cmd) { try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec(cmd, null, null); InputStream stderr = proc.getInputStream(); InputStreamReader isr = new InputStreamReader(stderr, "GBK"); BufferedReader br = new BufferedReader(isr); String line = ""; while ((line = br.readLine()) != null) { logger.info(line); } } catch (Exception e) { e.printStackTrace(); } }/** * 替换文本文件中的 非法字符串 * * @param path路径 * @param srcStr 原有的内容 * @param newStr 要替换的内容 */ public static void replacTextContent(String path, String srcStr, String newStr) { File file = new File(path); if (!file.exists() && !file.isFile()) { logger.info("file{},不存在:", path); return; } try { FileReader in = new FileReader(file); BufferedReader bufIn = new BufferedReader(in); // 内存流, 作为临时流 CharArrayWriter tempStream = new CharArrayWriter(); // 替换 String line = null; while ((line = bufIn.readLine()) != null) { // 替换每行中, 符合条件的字符串 line = line.replaceAll(srcStr, newStr); // 将该行写入内存 tempStream.write(line); // 添加换行符 tempStream.append(System.getProperty("line.separator")); } // 关闭 输入流 bufIn.close(); // 将内存中的流 写入 文件 FileWriter out = new FileWriter(file); tempStream.writeTo(out); out.close(); } catch (IOException e) { e.printStackTrace(); } logger.info("====path:" + path); }}

jmeter脚本 脚本其实也没有什么东西,只有定义好规则,这样方便替换。
性能|性能工具之 Jmeter 通过 SpringBoot 工程启动
文章图片

小结 上面 Demo 中还是一个问题没有解决就是在页面实时参看日志,目前还没实现,不过总体上实现自己想的功能。
源码地址:
  • https://github.com/zuozewei/blog-example/tree/master/Performance-testing/01-test-tool/jmeter/seven_days

    推荐阅读