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
点击上传脚本,弹出对话框,点击上传,后台日志显示上传成功。
点击启动,并且读取启动日志。
点击停止。
示意图说明:
通过访问 -> 调用 JAVA 代码-> 启动 shell命令-> 启动 Jmeter -> 获取启动日志
以下参考代码:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Jekyll v3.8.6">
<title>Jmeter启动</title>
<link rel="canonical" href="https://v4ing.bootcss.com/docs/4.3/examples/checkout/">
<!-- Bootstrap core CSS -->
<!-- Bootstrap core CSS -->
<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.3.1/css/bootstrap.css}" rel="stylesheet">
<!-- Favicons -->
<meta name="theme-color" content="#563d7c">
<style> .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; } } </style>
<!-- Custom styles for this template -->
<link href="https://v4.bootcss.com/docs/4.3/examples/checkout/form-validation.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container">
<div class="py-5 text-center">
<img src="https://jmeter.apache.org/images/logo.svg" class="d-block mx-auto mb-4" alt="Apache JMeter">
<p class="lead">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.</p>
</div>
<div class="col-md-8 order-md-1">
<h4 class="mb-3">上传脚本</h4>
<form>
<input id="jmeterId" type="file"/>
<a class="btn btn-lg btn-primary btn-block" value="上传脚本" onclick="submitupload()">上传脚本</a>
</form>
<form>
<input id="jmeterParam" type="file"/>
<a class="btn btn-lg btn-primary btn-block" value="上传参数" onclick="submitParm()">上传参数文件</a>
</form>
<h4 class="mb-3">运行</h4>
<!-- jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]-->
<form class="needs-validation" novalidate>
<div class="row">
<div class="col-md-6 mb-3">
<label for="jmeterName">压测脚本</label>
<input type="text" class="form-control" id="jmeterName" name="jmeterName" placeholder="jmx file" value="" required>
<div class="invalid-feedback">
jmx file
</div>
</div>
<div class="col-md-6 mb-3">
<label for="numberName">并发数</label>
<input type="text" class="form-control" id="numberName" name="num" placeholder="并发数" value="" required>
<div class="invalid-feedback">
并发数
</div>
</div>
<div class="col-md-6 mb-3">
<label for="duration">执行时间</label>
<input type="text" class="form-control" id="duration" name="duration" placeholder="执行时间" value="" required>
<div class="invalid-feedback">
并发数
</div>
</div>
</div>
<hr class="mb-4">
<a class="btn btn-success" onclick="JmeterRun()" type="submit">运行</a>
<a class="btn btn-danger" onclick="Jmeterstop()" type="submit">停止</a>
<a class="btn btn-info" onclick="JmeterInfo()" data-toggle="modal" data-target="#myModal">查看信息</a>
</form>
</div>
</div>
<!-- 日志模态框 -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Jmeter运行日志</h4>
</div>
<div class="modal-body">
<textarea rows="30" cols="20" id="JmeterMsg"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<!-- <button type="button" class="btn btn-primary">Save changes</button>-->
</div>
</div>
</div>
</div>
<footer class="my-5 pt-5 text-muted text-center text-small">
<p class="mb-1">© 2017-2019 Company Name</p>
<ul class="list-inline">
<li class="list-inline-item"><a href="#">Privacy</a></li>
<li class="list-inline-item"><a href="#">Terms</a></li>
<li class="list-inline-item"><a href="#">Support</a></li>
</ul>
</footer>
</div>
<script type="text/javascript" th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
<script th:src="@{/asserts/js/layer/layer.js}"></script>
<script>window.jQuery || document.write('<script src="/docs/4.3.1/assets/js/vendor/jquery.slim.min.js"><\/script>')</script>
<script src="/docs/4.3/dist/js/bootstrap.bundle.js" th:src="@{/webjars/bootstrap/4.3.1/js/bootstrap.bundle.js}"></script>
<script src="https://v4.bootcss.com/docs/4.3/examples/checkout/form-validation.js"></script>
</body>
<script> //上传脚本 function submitupload() { var type = "file"; //后台接收时需要的参数名称,自定义即可 var id = "jmeterId"; //即input的id,用来寻找值 var formData = 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 = "/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 = 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 () { }); } } }) } </script>
</html>
参考代码如下:
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);
}
}
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 = "<stringProp name=\"ThreadGroup.num_threads\">#numThread</stringProp>";
//执行时间
String time = "<stringProp name=\"ThreadGroup.duration\">#timeDuration</stringProp>";
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);
}
}
脚本其实也没有什么东西,只有定义好规则,这样方便替换。
上面 Demo 中还是一个问题没有解决就是在页面实时参看日志,目前还没实现,不过总体上实现自己想的功能。
源码地址:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://zuozewei.blog.csdn.net/article/details/121329689
内容来源于网络,如有侵权,请联系作者删除!