性能测试:JMeter 压测 Spring Boot 微服务

在微服务架构盛行的今天,Spring Boot 因简洁高效的特点成为构建微服务的首选框架。而微服务上线前,性能测试是保障其稳定运行的关键环节------它能提前发现系统在高并发、大数据量场景下的瓶颈,比如响应延迟、吞吐量不足、资源占用过高等问题。JMeter 作为 Apache 开源的性能测试工具,支持对 HTTP、FTP、数据库等多种场景进行压测,且配置灵活、易用性强,是测试 Spring Boot 微服务性能的理想选择。

本文将从「环境准备」「Spring Boot 微服务搭建」「JMeter 压测实战」「结果分析」「拓展知识」五个部分,带大家完整掌握 JMeter 压测 Spring Boot 微服务的核心流程,所有示例代码均可直接复用,语言通俗易懂,适合刚接触性能测试的开发者。

一、环境准备

工欲善其事,必先利其器。在开始压测前,我们需要搭建好基础环境,确保各工具版本兼容,避免因环境问题导致测试失败。

1.1 核心工具与版本

  • JDK:1.8 及以上(Spring Boot 2.x+ 推荐,JMeter 也依赖 JDK 运行)

  • Spring Boot:2.7.10(稳定版,兼容性好)

  • JMeter:5.6(最新稳定版,支持更多新特性)

  • 开发工具:IDEA 2022.3(可选,也可用 Eclipse)

  • 接口测试工具:Postman(辅助验证微服务接口可用性)

1.2 环境搭建步骤

1.2.1 JDK 配置

  1. 下载 JDK 1.8 或更高版本,安装后配置环境变量:
  • Windows:右键「此电脑」→「属性」→「高级系统设置」→「环境变量」,新建 JAVA_HOME,值为 JDK 安装路径;在 Path 中添加 %JAVA_HOME%\bin

  • Mac/Linux:编辑 ~/.bash_profile 或 ~/.zshrc,添加 export JAVA_HOME=JDK 安装路径,export PATH=JAVAHOME/bin:JAVA_HOME/bin:JAVAHOME/bin:PATH,执行 source 命令生效

  1. 验证:在终端输入 java -version,若显示版本信息则配置成功。

1.2.2 JMeter 安装与配置

  1. 下载 JMeter:从 Apache 官网 下载二进制包(apache-jmeter-5.6.zip),解压到任意目录(路径不含中文和空格)。

  2. 配置 JMeter 环境变量(可选,方便终端启动):

  • Windows:新建 JMETER_HOME,值为 JMeter 解压路径;在 Path 中添加 %JMETER_HOME%\bin

  • Mac/Linux:编辑配置文件,添加 export JMETER_HOME=JMeter 解压路径,export PATH=JMETERHOME/bin:JMETER_HOME/bin:JMETERHOME/bin:PATH

  1. 启动 JMeter:
  • Windows:双击 bin 目录下的 jmeter.bat

  • Mac/Linux:在终端进入 bin 目录,执行 ./jmeter.sh

启动后会进入 JMeter 图形化界面,首次启动可能会有弹窗提示「使用图形化界面仅用于测试创建和调试,压测时建议使用命令行模式」,后续我们会讲解两种模式的使用。

二、搭建 Spring Boot 微服务示例

为了模拟真实的微服务场景,我们搭建一个简单的「用户管理微服务」,包含「查询用户列表」「新增用户」两个核心接口(GET + POST 类型,覆盖常见的 HTTP 请求场景)。

2.1 创建 Spring Boot 项目

  1. 打开 IDEA,选择「New Project」→「Spring Initializr」,配置项目信息:
  • Group:com.example

  • Artifact:user-service

  • Package:com.example.userservice

  • Java Version:8

  • Spring Boot Version:2.7.10

  1. 选择依赖:勾选「Spring Web」(提供 HTTP 接口支持),点击「Create」完成创建。

2.2 编写核心代码

我们采用「内存存储」(避免引入数据库依赖,简化测试环境),用 List 模拟用户数据库,实现基础的 CRUD 接口。

2.2.1 实体类 User

在 com.example.userservice.entity 包下创建 User 类,封装用户信息:

java 复制代码
package com.example.userservice.entity;

import lombok.Data;

// Lombok 注解,自动生成 getter、setter、toString 方法(需引入 Lombok 依赖)
@Data
public class User {
    // 用户 ID
    private Long id;
    // 用户名
    private String username;
    // 用户年龄
    private Integer age;
    // 用户邮箱
    private String email;
}
    

注意:使用 Lombok 需在 pom.xml 中添加依赖(若创建项目时未勾选,手动添加):

xml 复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
    

2.2.2 接口 UserController

在 com.example.userservice.controller 包下创建 UserController 类,编写 HTTP 接口:

java 复制代码
package com.example.userservice.controller;

import com.example.userservice.entity.User;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/users")
public class UserController {
    // 模拟数据库(内存存储)
    private static final List<User> USER_LIST = new ArrayList<>();
    // 原子类,保证 ID 自增(避免并发场景下 ID 重复)
    private static final AtomicLong ID_GENERATOR = new AtomicLong(1);

    // 初始化 2 条测试数据
    static {
        User user1 = new User();
        user1.setId(ID_GENERATOR.getAndIncrement());
        user1.setUsername("zhangsan");
        user1.setAge(25);
        user1.setEmail("zhangsan@example.com");

        User user2 = new User();
        user2.setId(ID_GENERATOR.getAndIncrement());
        user2.setUsername("lisi");
        user2.setAge(30);
        user2.setEmail("lisi@example.com");

        USER_LIST.add(user1);
        USER_LIST.add(user2);
    }

    /**
     * 接口 1:查询所有用户列表(GET 请求)
     * 路径:/api/users
     * 无参数
     */
    @GetMapping
    public List<User> getUserList() {
        // 模拟业务处理延迟(10ms,更贴近真实场景)
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return USER_LIST;
    }

    /**
     * 接口 2:新增用户(POST 请求)
     * 路径:/api/users
     * 参数:User 对象(JSON 格式)
     */
    @PostMapping
    public User addUser(@RequestBody User user) {
        // 生成自增 ID
        user.setId(ID_GENERATOR.getAndIncrement());
        // 添加到模拟数据库
        USER_LIST.add(user);
        // 模拟业务处理延迟(15ms)
        try {
            Thread.sleep(15);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return user;
    }
}
    

2.2.3 启动类 UserServiceApplication

默认生成的启动类无需修改,确保注解 @SpringBootApplication 存在:

java 复制代码
package com.example.userservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }

}
    

2.3 验证微服务接口可用性

  1. 启动 Spring Boot 项目:运行 UserServiceApplication 类的 main 方法,默认端口为 8080(可在 application.properties 中修改,比如 server.port=8081)。

  2. 用 Postman 验证接口:

  • 验证 GET 接口(查询用户列表):

  • 请求地址:http://localhost:8080/api/users

  • 请求方式:GET

  • 预期结果:返回 2 条初始化用户数据,状态码 200 OK

验证 POST 接口(新增用户):

若接口均能正常返回结果,说明 Spring Boot 微服务搭建成功,可以开始 JMeter 压测了。

三、JMeter 压测实战

JMeter 压测的核心流程是:「创建测试计划」→「配置线程组」→「添加采样器(HTTP 请求)」→「添加监听器(用于查看结果)」→「执行压测」→「分析结果」。我们分别对上述两个接口进行压测,先讲解图形化界面操作(适合调试),再讲解命令行模式(适合正式压测)。

3.1 图形化界面压测(以 GET 接口为例)

3.1.1 创建测试计划

启动 JMeter 后,默认会创建一个「Test Plan」(测试计划),可直接修改名称(比如「User-Service-Performance-Test」)。测试计划是 JMeter 压测的顶层容器,包含所有压测相关的配置。

3.1.2 添加线程组

线程组是 JMeter 压测的核心组件,用于模拟并发用户:

  1. 右键「Test Plan」→「Add」→「Threads (Users)」→「Thread Group」

  2. 配置线程组参数(关键参数说明):

    Number of Threads (users):并发用户数(比如 100,表示同时有 100 个用户访问接口)

  3. Ramp-Up Period (in seconds):线程启动时间(比如 10,表示 10 秒内启动 100 个线程,避免瞬间压力过大)

  4. Loop Count:循环次数(比如 10,表示每个线程执行 10 次请求;勾选「Forever」表示无限循环,需手动停止)

  5. Delay Thread creation until needed:按需创建线程(建议勾选,节省资源)

  6. 本次测试配置(调试用):Number of Threads=50,Ramp-Up Period=5,Loop Count=20

3.1.3 添加 HTTP 请求采样器

采样器用于模拟具体的 HTTP 请求(即访问我们的微服务接口):

  1. 右键「Thread Group」→「Add」→「Samplers」→「HTTP Request」

  2. 配置 HTTP 请求参数(针对 GET 接口 /api/users):

    Name:HTTP Request - Get User List(自定义名称,便于区分)

  3. Protocol [http]: http(默认 http,若微服务用 HTTPS 则填 https)

  4. Server Name or IP:localhost(微服务部署地址,本地测试填 localhost)

  5. Port Number:8080(微服务端口)

  6. Method:GET(请求方式,与接口一致)

  7. Path:/api/users(接口路径,不含域名和端口)

  8. 其他参数默认即可(比如 Parameters、Body Data 等,GET 接口无需配置)

3.1.4 添加监听器(查看压测结果)

监听器用于收集和展示压测数据,常用的有「查看结果树」「聚合报告」「Summary Report」,建议同时添加:

  1. 右键「HTTP Request」→「Add」→「Listeners」→「View Results Tree」(查看结果树,可查看每个请求的详细信息,比如请求参数、响应数据、状态码)

  2. 右键「HTTP Request」→「Add」→「Listeners」→「Aggregate Report」(聚合报告,核心指标汇总,比如吞吐量、响应时间)

  3. 右键「HTTP Request」→「Add」→「Listeners」→「Summary Report」(摘要报告,简洁展示关键指标)

3.1.5 执行压测并查看结果

  1. 点击 JMeter 顶部工具栏的「启动」按钮(绿色三角形),开始压测。压测过程中,线程组会显示「Running」状态,监听器会实时更新数据。

  2. 压测结束后,查看核心监听器:

  • 查看结果树:筛选「Response Data」,可看到每个请求的响应结果(与 Postman 返回一致);若状态码为 200 则表示请求成功,若为 4xx/5xx 则表示接口异常。

  • 聚合报告:核心指标说明(重点关注):

    Sample:总请求数(50 线程 × 20 循环 = 1000 次)

  • Average:平均响应时间(单位:ms,越小越好)

  • Median:中位数响应时间(50% 的请求响应时间小于该值,更贴近真实用户体验)

  • 90% Line:90% 响应时间(90% 的请求响应时间小于该值,评估系统稳定性)

  • 95% Line/99% Line:同理,评估极端场景下的响应时间

  • Throughput:吞吐量(单位:requests/sec,每秒处理的请求数,越大越好)

  • Error %:错误率(错误请求数/总请求数,越低越好,理想值为 0)

3.1.6 POST 接口压测(补充配置)

POST 接口与 GET 接口的配置差异在于「请求体」,需添加 JSON 参数:

  1. 复制上述 HTTP 请求采样器,修改名称为「HTTP Request - Add User」。

  2. Method 改为「POST」。

  3. 添加请求头(JSON 格式需指定 Content-Type):

    右键「HTTP Request - Add User」→「Add」→「Config Elements」→「HTTP Header Manager」。

  4. 点击「Add」,添加参数:Name=Content-Type,Value=application/json。

  5. 配置请求体:在 HTTP Request 中找到「Body Data」,填入 JSON 数据:
    { "username": "testuser${__threadNum}", "age": ${__Random(20,40)}, "email": "test${__threadNum}@example.com" } 说明:使用 JMeter 内置函数实现参数化(避免新增用户重复):${__threadNum}:获取当前线程号(每个线程的用户名/邮箱唯一)

  6. ${__Random(20,40)}:生成 20-40 之间的随机数(模拟不同年龄)

  7. 重复上述「添加监听器」「执行压测」步骤,即可完成 POST 接口的压测。

3.2 命令行模式压测(正式环境推荐)

图形化界面会占用较多本地资源,可能影响压测结果的准确性,因此正式压测时建议使用「命令行模式」(无界面,资源占用少)。步骤如下:

3.2.1 保存测试计划

在图形化界面中,完成测试计划配置后,点击「File」→「Save」,将测试计划保存为 .jmx 文件(比如 user-service-test.jmx,建议保存到 JMeter 的 bin 目录下,方便命令行执行)。

3.2.2 执行命令行压测

  1. 打开终端,进入 JMeter 的 bin 目录(若配置了环境变量,可直接在任意目录执行)。

  2. 执行压测命令(核心参数说明):

bash 复制代码
jmeter -n -t 测试计划文件.jmx -l 结果输出文件.jtl -e -o 报告输出目录
    
  • -n:以命令行模式运行

  • -t:指定测试计划文件(.jmx)

  • -l:指定结果输出文件(.jtl,可后续导入图形化界面查看)

  • -e:生成 HTML 格式的测试报告

  • -o:指定 HTML 报告的输出目录(需为空目录,否则会报错)

  1. 示例命令(根据实际文件路径修改):
bash 复制代码
jmeter -n -t user-service-test.jmx -l user-service-result.jtl -e -o user-service-report
    
  1. 执行后,终端会实时输出压测进度(比如「Samples: 1000, Errors: 0, Time: 1234ms」)。压测结束后,会在 bin 目录下生成 .jtl 结果文件和 user-service-report 目录(HTML 报告)。

3.2.3 查看 HTML 报告

进入 user-service-report 目录,打开 index.html 文件(用浏览器打开),会看到可视化的测试报告,包含「Dashboard」「Charts」「Tables」等模块,可直观查看吞吐量、响应时间、错误率等核心指标,支持导出和分享。

四、压测结果分析与问题排查

压测的核心目的是发现问题,因此结果分析至关重要。我们以「GET 接口压测结果」为例,讲解常见问题的识别与排查思路。

4.1 核心指标参考标准(通用)

不同业务场景对性能的要求不同(比如秒杀场景 vs 后台管理系统),以下是通用参考标准(供参考):

  • 响应时间:Average < 300ms,90% Line < 500ms,99% Line < 1000ms

  • 吞吐量:根据业务需求,比如普通微服务需达到 100+ requests/sec

  • 错误率:Error % = 0(若存在错误,需优先排查)

  • 服务器资源:CPU 使用率 < 70%,内存使用率 < 80%(需结合服务器监控工具查看)

4.2 常见问题与排查思路

4.2.1 错误率过高(Error % > 0)

  • 可能原因:

    微服务接口异常(比如代码 bug、内存溢出)

  • JMeter 配置错误(比如请求方式、路径、请求头错误)

  • 服务器资源耗尽(CPU/内存/端口占用过高)

排查步骤:

查看 JMeter 「查看结果树」,筛选错误请求,查看「Response Data」中的错误信息(比如 500 错误提示「NullPointerException」)。

查看 Spring Boot 微服务的日志(IDEA 控制台或日志文件),定位代码异常位置。

用服务器监控工具(比如 top 命令、JConsole)查看服务器 CPU、内存使用率。

4.2.2 响应时间过长(Average > 500ms)

  • 可能原因:

    微服务代码效率低(比如循环嵌套过多、未使用缓存)

  • 服务器配置过低(比如本地测试用的是普通笔记本,而非服务器)

  • 并发用户数过高(超过系统承载能力)

排查步骤:

减少并发用户数,重新压测,观察响应时间是否改善(判断是否为并发过高导致)。

使用 JProfiler 等性能分析工具,分析 Spring Boot 微服务的方法执行时间,定位耗时过长的方法。

优化代码(比如添加缓存、优化数据库查询)或升级服务器配置。

4.2.3 吞吐量过低(Throughput < 50 requests/sec)

吞吐量与响应时间密切相关(响应时间越短,吞吐量越高),排查思路与「响应时间过长」一致,核心是优化微服务性能和服务器配置。

五、拓展知识(提升压测专业性)

掌握基础压测流程后,以下拓展知识能帮助你应对更复杂的微服务场景(比如分布式微服务、带认证的接口、大数据量场景)。

5.1 JMeter 进阶配置

5.1.1 参数化进阶(CSV 数据文件设置)

当需要大量不同的测试数据(比如 1000 个不同的用户名)时,用内置函数不够灵活,可使用「CSV 数据文件设置」:

  1. 创建 CSV 文件(比如 users.csv),内容格式:username,age,email

  2. 右键「HTTP Request」→「Add」→「Config Elements」→「CSV Data Set Config」。

  3. 配置参数:

    Filename:CSV 文件路径(比如 D:/users.csv)

  4. Variable Names:username,age,email(与 CSV 表头一致)

  5. Delimiter:,(CSV 文件分隔符)

  6. Recycle on EOF:True(数据读完后循环使用)

  7. Stop thread on EOF:False(数据读完后不停止线程)

  8. 在 HTTP 请求体中引用变量:username、{username}、username、{age}、${email}。

5.1.2 定时器(模拟真实用户思考时间)

真实用户访问接口时,会有思考时间(比如查询完用户列表后,停顿 1-2 秒再新增用户),可通过「定时器」模拟:

  • 右键「Thread Group」→「Add」→「Timers」→「Constant Timer」(固定思考时间,比如 1000ms)。

  • 或选择「Gaussian Random Timer」(高斯随机时间,更贴近真实场景,比如平均 1000ms,偏差 300ms)。

5.1.3 断言(自动验证响应结果)

压测时手动查看响应结果效率低,可通过「断言」自动验证响应是否符合预期(比如响应中包含「username」字段):

  1. 右键「HTTP Request」→「Add」→「Assertions」→「JSON Path Assertion」(JSON 格式接口推荐)。

  2. 配置参数:

    JSON Path:$[0].username(断言第一个用户的 username 字段存在)

  3. Expected Value:zhangsan(预期值,可选)

  4. Match as Regular Expression:False(是否作为正则匹配)

  5. Expect null:False

  6. Invert assertion:False(是否反向断言)

  7. 若断言失败,该请求会被标记为错误(在监听器中显示)。

5.2 Spring Boot 微服务性能优化建议

压测发现性能瓶颈后,可从以下角度优化 Spring Boot 微服务:

    1. 接口优化:
      使用缓存(比如 Redis)缓存高频查询数据(如用户列表),减少重复计算。
  • 异步处理非核心业务(比如新增用户后发送通知,用 @Async 注解异步执行)。
  1. JVM 调优:
    调整 JVM 堆内存(比如 -Xms2g -Xmx2g,初始堆内存和最大堆内存一致,避免频繁 GC)。

选择合适的垃圾收集器(比如 G1 收集器,适合大内存场景)。

  1. 服务器优化:使用 Tomcat 线程池(在 application.properties 中配置 server.tomcat.threads.max=200)。

部署多个微服务实例,通过 Nginx 负载均衡分担压力。

5.3 分布式微服务压测注意事项

若压测的是分布式微服务(比如用户服务、订单服务、支付服务联动),需注意:

    1. 模拟真实调用链路:在 JMeter 中添加多个 HTTP 请求采样器,按业务流程排序(比如先查询用户,再创建订单,最后支付)。
    1. 避免单点瓶颈:确保所有依赖服务(比如数据库、Redis、消息队列)都部署在测试环境,且配置与生产环境一致。
    1. 使用分布式压测工具:若单台 JMeter 无法模拟足够的并发用户(比如 10000+ 并发),可使用 JMeter 分布式压测(多台 slave 机器同时压测,master 机器汇总结果)。

六、总结

本文从环境搭建到实战演练,完整讲解了 JMeter 压测 Spring Boot 微服务的核心流程:首先搭建了基础的用户管理微服务,然后通过 JMeter 图形化界面调试接口压测,再用命令行模式进行正式压测,最后讲解了结果分析和进阶拓展知识。

性能测试是一个持续优化的过程------不仅需要掌握工具的使用,更需要结合业务场景分析问题、优化系统。建议大家在实际工作中,结合本文的示例代码,针对自己的微服务进行压测演练,逐步积累性能优化经验。

如果在压测过程中遇到问题,可查看 JMeter 官方文档或 Spring Boot 性能优化相关资料,也可以留言交流~

相关推荐
算法与双吉汉堡8 小时前
【短链接项目笔记】Day1 用户模块
java·spring boot·笔记·后端
Selegant8 小时前
Quarkus vs Spring Boot:谁更适合云原生时代的 Java 开发?
java·spring boot·云原生
大猫和小黄8 小时前
Windows环境下使用Nacos搭建若依(RuoYi)微服务版完整指南
windows·微服务·架构
计算机毕设指导68 小时前
基于微信小程序的校园食堂点餐系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij-idea
Mr.Pascal8 小时前
深度解读一下 springcloud 的 pom.xml 用到的标签
xml·spring boot·spring cloud
Roye_ack8 小时前
【微服务 Day1】SpringCloud实战开发(Mybatis-plus + Docker)
spring cloud·docker·微服务·mybatis
Qiuner8 小时前
Spring Boot AOP(二) 代理机制解析
java·spring boot·后端
拾忆,想起8 小时前
Dubbo RPC 实战全流程:从零搭建高可用微服务系统
网络·网络协议·微服务·性能优化·rpc·架构·dubbo