⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/yangshangwei 「小小工匠」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

引言

为何提前暴露指标与分析的重要性

在正式进行性能优化之前,必须先“看得到”系统运行状况:缓存命中率、数据库连接池使用情况、响应时长分布、CPU/内存消耗、垃圾回收停顿等。只有掌握真实数据,才能有针对性地优化;盲目调整往往事倍功半,甚至适得其反。

指标暴露与监控接入

Prometheus 集成

1.Maven 依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

2.application.properties/application.yml 配置

management.endpoint.metrics.enabled=true
management.endpoint.prometheus.enabled=true
management.endpoints.web.exposure.include=health,info,prometheus,metrics
management.metrics.export.prometheus.enabled=true
management.endpoint.health.show-details=always
# 可根据需要开放其他端点,如 httptrace、threaddump

3.启动与访问

启动后,访问 http://<host>:<port>/actuator/prometheus 可看到所有默认与自定义指标。

配置 Prometheus server 抓取该 endpoint,例如在 prometheus.yml:

scrape_configs:
- job_name: 'springboot-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app-host:port']

4.自定义业务指标示例

@RestController
public class TestController {
private final MeterRegistry registry;
public TestController(MeterRegistry registry) {
this.registry = registry;
}
@GetMapping("/test")
public String test() {
registry.counter("app_test_invocations", "from", "127.0.0.1", "method", "test").increment();
return "ok";
}
}

在 Prometheus 中可见指标如 app_test_invocations_total{from="127.0.0.1",method="test"} 5.0

对于缓存命中率,可在缓存拦截或 CacheManager 事件中注册 Counter/Gauge,例如:

Cache<Object, Object> cache = ...; // 若使用 Caffeine,可 attach 监听
// 当命中时 registry.counter("cache_hit", "cache", "myCache").increment();
// 当未命中时 registry.counter("cache_miss", "cache", "myCache").increment();

5.Grafana 可视化与 AlertManager

在 Grafana 中配置 Prometheus 数据源,创建 Dashboard 展示:

  • JVM 内存、GC 停顿、线程数
  • HTTP 请求速率、延迟分布(可借助 Histogram/Summary)
  • 缓存命中率:hit/(hit+miss)
  • 数据库连接池使用率:活跃连接数 vs 最大连接数

AlertManager 配置告警规则,例如:

  • 95% 延迟超过阈值
  • GC 停顿时长过长
  • 连接池耗尽告警

此部分可参考 Prometheus 与 Grafana 官方文档,自行搭建实验环境。

性能剖析工具:火焰图与 async-profiler

async-profiler 下载与使用

1.从 GitHub 下载 async-profiler release 包,解压至服务器目录,如 /opt/async-profiler。

2.启动 SpringBoot 应用时,添加 javaagent 参数:

java -agentpath:/opt/async-profiler/build/libasyncProfiler.so=start,svg,file=profile.svg -jar your-app.jar

3.运行一段业务场景(压测或真实流量),然后停止进程或通过 async-profiler 提供的 CLI 停止采样:

# 若不想重启,可 attach 模式:
./profiler.sh -d 30 -f profile.svg <pid>

4.查看生成的 profile.svg,用浏览器打开:

  • 横轴表示消耗的采样比例,宽度越大表示更耗时的方法。
  • 纵向为调用栈层级。
  • 从最宽处逐层向下分析,找到热点方法,结合源代码定位优化点。

结合 Flame 图优化示例

  • 若热点在某个慢方法,可查看方法内部逻辑:是否存在不必要的循环、I/O 阻塞,或可并行优化。
  • 若热点在序列化/反序列化,可考虑更高效的库或减小返回对象结构。
  • 若大量时间在 GC,可结合 GC 日志分析堆配置是否合理。

HTTP 及 Web 层优化

CDN 与静态资源加速

  • 将常用静态资源(JS、CSS、图片)托管于 CDN,减轻后端压力;用户访问时由地理就近节点提供。
  • 对第三方库可使用公共 CDN;自有资源可上传到 CDN 或静态文件服务器。

Cache-Control/Expires 在 Nginx 中配置示例

location ~* \.(ico|gif|jpg|jpeg|png|css|js)$ {
add_header Cache-Control "max-age=31536000, immutable";
}

根据资源更新策略设置版本号或文件指纹,保证缓存命中又可及时更新。

减少域名数量

  • 将静态资源和 API 接口合理合并域名;避免过多不同子域导致 DNS 查询延迟。
  • 可启用 HTTP/2 时,多路复用可减少域名依赖,但 HTTP/2 支持需服务器和客户端皆启用。

Gzip 及资源压缩配置

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_comp_level 6;
gzip_http_version 1.1;
gzip_types text/plain application/javascript text/css application/json;
  • 确保对动态 JSON 接口开启 gzip,减小响应体体积。
  • 对大型静态文件可在构建时先行压缩(如 Brotli),结合 Nginx 支持。

Keep-Alive 配置

客户端与 Nginx:

http {
keepalive_timeout 60s 60s;
keepalive_requests 10000;
}

Nginx 与 SpringBoot 后端长连接:

location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}

SpringBoot/Tomcat 默认支持 Keep-Alive;可通过自定义 Connector 调整超时。

SpringBoot 容器调优

自定义嵌入式 Tomcat

如果项目并发量比较高,想要修改最大线程数、最大连接数等配置信息,可以通过自定义Web 容器的方式,代码如下所示。

@SpringBootApplication(proxyBeanMethods = false)
publicclass App implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (factory instanceof TomcatServletWebServerFactory) {
TomcatServletWebServerFactory f = (TomcatServletWebServerFactory) factory;
f.setProtocol("org.apache.coyote.http11.Http11Nio2Protocol");
f.addConnectorCustomizers(c -> {
Http11NioProtocol protocol = (Http11NioProtocol) c.getProtocolHandler();
protocol.setMaxConnections(200);
protocol.setMaxThreads(200);
protocol.setConnectionTimeout(30000);
// protocol.setSelectorTimeout(3000); // NIO2 可选项
});
}
}
}

设置协议为 NIO2 可在高并发 I/O 场景下提升性能;需基准测试验证。

setMaxThreads 和 setMaxConnections 值根据服务器硬件资源与业务并发调整,可在压测环境多次尝试并观察响应延迟、CPU 利用率、线程使用情况。

替换 Undertow

在 pom.xml 中排除 Tomcat 并引入 Undertow:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
  • Undertow 线程模型和资源占用一般较轻;需要在业务场景中压测对比。
  • 注意部分 Tomcat 特有配置不适用,需调整监控指标对应项。

JVM 参数回顾

结合性能优化 - 高级进阶:JVM 常见优化参数,可启动时传入:

-XX:+UseG1GC -Xms2048m -Xmx2048m -XX:+AlwaysPreTouch -XX:MaxMetaspaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=512m
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dumps -XX:ErrorFile=/path/to/hs_err_pid%p.log
-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=5,filesize=100m

根据硬件和应用特点调整堆大小;若GC停顿问题突出,可结合 async-profiler 和 GC 日志深入分析。

应用性能监控与分布式追踪

SkyWalking 集成

1.下载与部署

  • 下载 SkyWalking Agent 和后端服务(按存储类型,如 Elasticsearch 存储版)。
  • 部署 SkyWalking 后端:配置 Storage、UI、Collector。

2.启动应用时添加 Agent

java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=your-service-name \
-Dskywalking.collector.backend_service=collector-host:11800 \
-jar your-app.jar

3.查看调用链与指标

  • 访问 SkyWalking UI,可看到每次请求的调用链图、各段耗时、数据库/HTTP 调用详情、JVM 指标、GC 指标。
  • 结合 Prometheus 等指标,综合判断:若某接口响应慢,可查看是哪一段(查询、序列化、远程调用等)成为瓶颈。

4.报警与自动化

  • 可结合 SkyWalking 的告警和 Prometheus AlertManager 设置阈值告警。
  • 定期分析热点接口和突发异常请求流,制定优化计划。

各层优化思路

Controller 层

  • DTO 精简与分页: 避免一次性返回过大结果集;对列表数据使用分页或流式方式(如 Spring MVC StreamingResponseBody)。
  • JSON 序列化优化: 选择高性能序列化库(Jackson 已较快,可开启 Afterburner 模块),避免将不必要字段序列化。
  • 输入校验与限流: 对异常或恶意请求提前拦截,减少无效处理。
  • 幂等性与缓存: 对可缓存接口,如 GET 查询,结合 HTTP 缓存头或后端缓存减少重复计算。

Service 层

  • 无状态设计:Service Bean 默认单例无状态,避免在 Bean 中维护请求级状态。

  • 合理拆分与职责分离: 将复杂逻辑拆成小模块,便于监控与优化。

  • 异步与并行: 对于可并行处理的子任务,可使用 CompletableFuture、异步消息队列等;但要注意线程池配置与上下文传递。

  • 缓存策略:

    • 本地缓存(Caffeine): 低延迟、减轻远程调用压力;监控命中率,避免缓存污染。
    • 分布式缓存(Redis): 适用于共享场景;注意 TTL 策略、防止缓存穿透(布隆过滤、请求预热)、防止雪崩(加随机过期)、防止击穿(互斥锁或预加载)。
  • 避免重复计算与请求合并: 对重复请求可合并或去重,减少下游压力。

  • 分布式事务讨论:

    • 两阶段提交、TCC、本地消息表、MQ事务消息等方案都会增加延迟与资源占用;仅在必要场景使用。

优先考虑补偿事务与柔性事务,实现最终一致性;通过幂等设计与补偿逻辑将风险和性能开销控制在可接受范围内。

如上图,分布式事务要在改造成本、性能、时效等方面进行综合考虑。有一个介于分布式事务和非事务之间的名词,叫作柔性事务。柔性事务的理念是将业务逻辑和互斥操作,从资源层上移至业务层面。

传统事务 vs 柔性事务

关于传统事务和柔性事务,我们来简单比较一下。

ACID

关系数据库,最大的特点就是事务处理,即满足 ACID。

  • 原子性(Atomicity): 事务中的操作要么都做,要么都不做。
  • 一致性(Consistency): 系统必须始终处在强一致状态下。
  • 隔离性(Isolation): 一个事务的执行不能被其他事务所干扰。
  • 持久性(Durability): 一个已提交的事务对数据库中数据的改变是永久性的。

BASE

BASE 方法通过牺牲一致性和孤立性来提高可用性和系统性能。

BASE 为 Basically Available、Soft-state、Eventually consistent 三者的缩写,其中 BASE 分别代表:

  • 基本可用(Basically Available): 系统能够基本运行、一直提供服务。
  • 软状态(Soft-state): 系统不要求一直保持强一致状态。
  • 最终一致性(Eventual consistency): 系统需要在某一时刻后达到一致性要求。

互联网业务,推荐使用补偿事务,完成最终一致性。比如,通过一系列的定时任务,完成对数据的修复。

DAO 层

ORM 使用注意:

  • 懒加载原则: 避免无意触发大量关联查询,合理使用 FetchType.LAZY,并在Service层通过显式查询或 DTO 投影避免 N+1。
  • 批量操作: 对于大量写场景,使用批量插入/更新。

SQL 优化与索引: 分析慢查询日志、使用 EXPLAIN 确认索引命中,避免全表扫描。

分库分表注意: 理解中间件实现原理,避免误以为简单 SQL 实现即高效;关注路由开销与合并成本。

数据库连接池:

  • HikariCP 默认已非常高效,但仍需监控活跃连接数、等待时长;根据业务并发调整最大连接数,避免连接池耗尽或空闲过多浪费资源。
  • 在 Prometheus 中可通过 HikariPool 指标收集连接使用情况,适时调整。

缓存优化

Caffeine: 适合本地缓存,低延迟;需监控缓存大小、命中率、加载延迟;避免过大导致内存占用过高。

Redis:

  • 连接池配置: 监控和调整 Lettuce/Redisson 连接数;注意阻塞或过度并发导致连接耗尽。
  • 数据序列化: 选择高效序列化方式(如 JSON、Kryo、FST 等),兼顾可读性与性能。
  • 缓存策略: 参见上文防护策略;结合业务场景设计合理 TTL。

二级缓存: 对数据库读密集应用,可考虑本地+分布式二级缓存架构;注意缓存一致性。

资源与线程管理

线程池配置:

  • 对于异步任务、自定义 Executor,设置合适的 core/max pool size、queue size;监控线程活跃度、队列长度,防止任务堆积。
  • 对于定时任务,避免过多定时线程争抢资源。

非阻塞与异步:

  • 若业务适合,可使用 WebFlux 或 Reactor,但需慎重考虑团队熟悉度与实际场景;异步场景要注意线程切换开销和上下文传递。
  • 对外 HTTP 调用可使用异步 HTTP 客户端(如 WebClient),避免阻塞线程。

端到端测试与压测

  • 压测工具: wrk、JMeter、Locust 等
  • 测前准备: 确保监控、日志、profiling 准备完毕;在测试环境部署与生产相近的架构。
  • 测试脚本设计: 模拟真实业务流量,包括登录、查询、写入等混合场景。
  • 结果分析: 结合 Prometheus/Grafana 监控数据和火焰图,找出瓶颈;反复调优并回归测试,评估改动效果。
  • 负载模型: 考虑渐增负载测试、稳定性测试、持久压力测试,观察资源消耗与响应曲线。

小结

  • 指标采集—剖析—优化—验证是闭环流程: 始终保持对系统可观测性。
  • 渐进式改动: 在非生产环境验证,避免一次性大改带来风险;生产环境小步部署与监控回滚准备。
  • CI/CD 集成: 可在持续集成/部署流程中集成简单的健康检查和性能基准测试。
  • 定期回顾: 定时检查关键接口的性能指标,防止代码或依赖升级带来性能回退。
  • 团队协作: 文档化优化经验,分享给团队成员,形成共识和规范。

文章目录
  1. 1. 引言
  2. 2. 指标暴露与监控接入
    1. 2.1. Prometheus 集成
  3. 3. 性能剖析工具:火焰图与 async-profiler
    1. 3.1. async-profiler 下载与使用
    2. 3.2. 结合 Flame 图优化示例
  4. 4. HTTP 及 Web 层优化
    1. 4.1. CDN 与静态资源加速
    2. 4.2. Cache-Control/Expires 在 Nginx 中配置示例
    3. 4.3. 减少域名数量
    4. 4.4. Gzip 及资源压缩配置
    5. 4.5. Keep-Alive 配置
  5. 5. SpringBoot 容器调优
    1. 5.1. 自定义嵌入式 Tomcat
    2. 5.2. 替换 Undertow
    3. 5.3. JVM 参数回顾
  6. 6. 应用性能监控与分布式追踪
    1. 6.1. SkyWalking 集成
  7. 7. 各层优化思路
    1. 7.1. Controller 层
    2. 7.2. Service 层
    3. 7.3. DAO 层
    4. 7.4. 缓存优化
    5. 7.5. 资源与线程管理
    6. 7.6. 端到端测试与压测
  8. 8. 小结