[转]性能工具之Jmeter通过springboot工程启动

背景

Jmeter平时性能测试工作一般都是通过命令行在linux下执行,为了锻炼自己代码与逻辑能力,想jmeter是否可以通过springboot工程启动,周末在家尝试写一写,一写原来需要处理很多事情,才可以启动起来,起来还是有很问题需要处理,下面是相应的代码,其实网上也有,但关键的是自己有意识收集知识,到用的时候能拿来改一改就用。

启动页面:

 

前置条件

1
2
3
4
5
6
7
8
9
10
11
需要在linux中配置Jmeter成功,并且配置环境变量:
 
环境配置:
编辑:
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-获取启动日志

 

 

前端代码

以下参考代码,大家可以学习学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<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>
 
 
<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>

  

后端Controller

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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 liwen
 * @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层代码

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * @author liwen
 * @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();
 
}

  

接口实现层

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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 liwen
 * @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 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;
    }
 
 
}

  

工具类

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * 直接执行命令
 *
 * @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();
    }
}

  

jmeter脚本:

脚本其实也没有什么东西,只有定义好规则,这样方便替换。

 

 

GitHub 地址:

https://github.com/357712148/bodygit.git

 

 

小结:

做性能测试代码能力,不是关键,但是是晋升一个必要条件,而且在项目性能分析还是需要懂一些代码能力,这样与研发,DBA、运维能谈的来。

善用时间,珍惜时间意味着生命的延长,人生的卓越。

上面存在的问题:

上面deme中还是一个问题没有解决就是在页面实时参看日志,目前还没实现,不过总体上实现自己想的功能。

 

 

posted @   AlamZ  阅读(553)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示