简介:《Spring Boot Metrics:深入理解与应用》介绍了Spring Boot框架中的核心组件spring-boot-metrics,它通过集成Actuator实现对应用程序运行时指标的自动采集与监控。该工具可收集HTTP请求、JVM内存、线程状态、数据库查询等关键性能数据,并提供标准化端点如/actuator/metrics和/actuator/prometheus供外部系统读取。支持与Prometheus、Grafana等主流监控平台无缝集成,实现指标可视化。开发者还可通过@Timed、@Counted等注解及MeterRegistry接口自定义业务级度量指标,全面提升系统的可观测性与可维护性。本项目经过完整测试,适用于开发、测试与生产环境下的性能分析与故障排查。
Spring Boot Metrics 全链路可观测性实战指南
你有没有遇到过这种情况:线上服务突然变慢,用户投诉接踵而至,但日志里翻来覆去就那几行“Request processed”,根本看不出问题在哪?或者某个接口 QPS 暴涨,CPU 直冲 100%,却不知道是哪个 URI 在作祟?
🤔 “要是能一眼看出每个接口的调用量、延迟分布,甚至 JVM 内存变化趋势就好了。”
别急——这正是我们今天要深入探讨的内容。 Spring Boot Metrics + Actuator + Prometheus + Grafana 这套组合拳,就是现代 Java 应用的“听诊器”和“X光机”。它不仅能告诉你“哪里疼”,还能帮你找到“病根”。
让我们从一个最简单的场景开始:你的微服务上线了,你想知道:
- 刚刚部署后,到底有多少人在调用
/api/user? - 平均响应时间是多少?有没有出现超时?
- 系统内存使用是否正常?GC 频率高不高?
这些问题的答案,其实都藏在 spring-boot-starter-actuator 提供的指标中。而这一切,只需要几行配置就能搞定。
一、Metrics 是什么?为什么说它是系统的“生命体征”
在微服务架构下,系统就像一个人体,由多个器官(服务)协同工作。如果某个器官出了问题,比如心脏跳得太快或太慢,我们就需要通过心电图、血压计等设备监测它的状态。
那么,在软件世界里,谁来当这个“医生”?
答案是: Metrics(指标) 。
Metrics 就是你应用的“生命体征”——它们记录了从 HTTP 请求到 JVM 内存、线程数、CPU 使用率等一切运行时数据。没有这些数据,你就像是在黑暗中开车,只能靠运气避障。
而 Spring Boot 基于 Micrometer 构建了一套统一的指标抽象层,屏蔽了不同监控系统(如 Prometheus、Graphite、Datadog)之间的差异。这意味着你可以做到:
✅ 一次埋点,多端上报
无论你未来换哪家监控平台,代码几乎不用改!
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("application", "user-service");
}
上面这段代码做了件小事:为所有指标自动加上 application=user-service 标签。但它带来的价值可不小——当你有几十个微服务时,靠这个标签就能快速区分来源,再也不用猜“这条数据是谁发的”。
四大核心指标类型,你知道怎么用吗?
| 类型 | 英文名 | 用途 | 示例 |
|---|---|---|---|
| 计数器 | Counter | 单调递增,适合统计总数 | 请求次数、错误数 |
| 瞬时值 | Gauge | 反映当前状态 | 内存使用量、线程数 |
| 耗时记录 | Timer | 统计方法执行时间 | 接口响应时间 |
| 分布摘要 | DistributionSummary | 记录事件大小分布 | 请求 Body 大小 |
举个例子,假设你要监控一个下单接口:
@PostMapping("/order")
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
Timer.Sample sample = Timer.start(meterRegistry); // 开始计时
try {
Order saved = orderService.save(order);
return ResponseEntity.ok(saved);
} finally {
sample.stop(Timer.builder("order.create.duration") // 停止并上报
.tag("status", "success")
.register(meterRegistry));
}
}
这样,每创建一个订单,就会生成一条耗时指标,你可以轻松分析 P95/P99 延迟,找出性能瓶颈。
是不是比手动写 System.currentTimeMillis() 清爽多了?😎
二、如何让 Actuator “活”起来?从依赖引入到端点暴露
你以为加个 starter 就万事大吉了?错!很多团队明明引入了 spring-boot-starter-actuator ,结果 /actuator/metrics 还是 404,一脸懵圈。
问题出在哪? 默认安全策略 。
Spring Boot 出于安全考虑,默认只开放 /health 和 /info 两个基础端点。其他统统隐藏。所以你得主动“点亮”它们。
2.1 引入 actuator 起步依赖:Maven vs Gradle
这是第一步,也是最容易忽略的一环。
Maven 用户 👇
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
只要父 POM 继承自 spring-boot-starter-parent ,版本会自动对齐,无需显式指定。
Gradle 用户 👇
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
如果你用了 Kotlin DSL:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
}
💡 建议 :不要单独引入
micrometer-core或spring-boot-actuator-autoconfigure,直接用 Starter 最省事,避免版本冲突。
依赖树长什么样?
跑一下命令看看:
mvn dependency:tree | grep actuator
你会看到类似输出:
[INFO] +- org.springframework.boot:spring-boot-starter-actuator:jar:3.2.0:compile
[INFO] | +- org.springframework.boot:spring-boot-actuator-autoconfigure:jar:3.2.0:compile
[INFO] | | \- org.springframework.boot:spring-boot-actuator:jar:3.2.0:compile
[INFO] | \- io.micrometer:micrometer-observation:jar:1.12.0:compile
关键模块有三个:
| 模块 | 作用 |
|---|---|
spring-boot-actuator-autoconfigure | 自动装配主类,启动时扫描并注册端点 |
spring-boot-actuator | 实现 HealthEndpoint、MetricsEndpoint 等核心功能 |
micrometer-* | Micrometer 指标框架,负责数据建模与上报 |
这种分层设计实现了职责分离:上层管暴露,底层管采集。
2.2 自动装配机制揭秘:Spring Boot 是怎么“发现”你的端点的?
很多人以为加个注解就完事了,其实背后有一整套自动装配流程。
当 JVM 启动时,Spring Boot 会扫描类路径下的:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
这个文件里列着所有自动配置类。其中就有:
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration
一旦检测到 spring-boot-starter-actuator 存在,就会触发加载。
接下来发生的事可以用一张 Mermaid 流程图清晰表达:
flowchart TD
A[应用启动 SpringApplication.run()] --> B{Classpath 中是否存在<br>spring-boot-starter-actuator?}
B -->|是| C[加载 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports]
C --> D[发现 ServletManagementContextAutoConfiguration]
D --> E[注册 /actuator 映射的 DispatcherHandler]
E --> F[扫描 @Endpoint 注解的 Bean]
F --> G[创建 HealthEndpoint, MetricsEndpoint 等实例]
G --> H[通过 @WebEndpointExtension 暴露为 HTTP 端点]
H --> I[/actuator/metrics 可访问]
整个过程完全自动化,开发者无感完成。
关键配置类解析
| 类名 | 作用 | 条件注解 |
|---|---|---|
MetricsAutoConfiguration | 初始化 MeterRegistry (默认为 CompositeMeterRegistry) | @ConditionalOnClass(MeterRegistry.class) |
WebMvcEndpointManagementContextConfiguration | 在 Web 环境中注册 MVC 端点适配器 | @ConditionalOnClass(DispatcherServlet.class) |
HealthEndpointAutoConfiguration | 创建 HealthIndicator 集合并组装成 HealthEndpoint | @ConditionalOnEnabledEndpoint(endpoint = HealthEndpoint.class) |
以 MetricsAutoConfiguration 为例,其简化源码如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Timed.class)
@EnableConfigurationProperties(MetricsProperties.class)
public class MetricsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MeterRegistry meterRegistry() {
return new CompositeMeterRegistry();
}
@Bean
public MetricsRestTemplateCustomizer metricsRestTemplateCustomizer(
MeterRegistry registry) {
return new MetricsRestTemplateCustomizer(registry);
}
}
逐行解读:
-
@Configuration(proxyBeanMethods = false):禁用 CGLIB 代理,提升性能; -
@ConditionalOnClass(Timed.class):只有存在Timed注解才启用; -
@EnableConfigurationProperties(MetricsProperties.class):绑定management.metrics.*配置项; -
meterRegistry()返回一个复合注册中心,支持同时向多个后端发送数据; -
@ConditionalOnMissingBean表示允许用户自定义更高优先级的MeterRegistry。
这就给了你足够的扩展空间,比如替换为特定厂商的实现。
三、让 metrics 端点真正可用:暴露 + 安全控制 + 路径映射
现在依赖也加了,自动装配也走了,为啥还是看不到指标?
因为你还差最后一步: 显式暴露端点 。
3.1 application.yml 中的关键配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
metrics:
enabled: true
prometheus:
enabled: true
重点来了:
-
management.endpoints.web.exposure.include:列出要暴露的端点,支持逗号分隔或通配符*; -
management.endpoint.metrics.enabled:是否启用 metrics 端点本身; - 推荐生产环境 不要用
*,而是明确列出所需端点,防止敏感信息泄露。
| 配置项 | 默认值 | 说明 |
|---|---|---|
include | health,info | 要暴露的端点列表 |
exclude | 空 | 明确排除某些端点(优先级高于 include) |
management.server.port | 与主服务相同 | 可选:为管理端点指定独立端口 |
举个增强隔离性的例子:
server.port: 8080
management.server.port: 8081
management.server.address: 127.0.0.1
这样一来, /actuator/metrics 只能在内网通过 http://localhost:8081/actuator/metrics 访问,彻底阻断外部探测。
🔐 安全是底线,别图省事把监控端口暴露在公网!
3.2 安全加固:结合 Spring Security 控制访问权限
暴露之后怎么办?当然是上锁!
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后编写安全配置类:
@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
.requestMatchers("/actuator/**").hasRole("ACTUATOR")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin123")
.roles("ACTUATOR")
.build();
return new InMemoryUserDetailsManager(admin);
}
}
逻辑拆解:
-
/actuator/health和/actuator/info允许匿名访问,方便探针探测; - 其他所有
/actuator/**路径必须携带ACTUATOR角色; - 使用 Basic Auth,适合机器通信;
- 用户存储仅用于演示,生产环境应对接 LDAP/OAuth2。
测试一下:
curl -u admin:admin123 http://localhost:8080/actuator/metrics
返回 200 才算成功。
3.3 端点命名与 URL 映射规则,你真的懂吗?
Spring Boot 对 @Endpoint(id="xxx") 的类会自动映射为 /actuator/xxx 路径。
| 端点 ID | 默认路径 | 是否可改 |
|---|---|---|
| health | /actuator/health | 是 |
| metrics | /actuator/metrics | 是 |
| prometheus | /actuator/prometheus | 是 |
| threaddump | /actuator/threaddump | 是 |
可以通过以下方式修改:
management:
endpoints:
web:
path-mapping:
health: status
metrics: monitoring/metrics
此时新路径变为:
-
/actuator/status -
/actuator/monitoring/metrics
还可以改前缀:
base-path: /manage
最终变成 /manage/metrics 。
自定义端点示例
@Endpoint(id = "custom-stats")
public class CustomStatsEndpoint {
@ReadOperation
public Map<String, Integer> showStats() {
return Map.of("requests", 1000, "errors", 5);
}
}
默认路径是 /actuator/custom-stats ,想改成 /actuator/stats-summary 怎么办?
management:
endpoints:
web:
path-mapping:
custom-stats: stats-summary
搞定!🎉
四、运行时验证与常见问题排查:别让配置白搭
配置写完了,怎么确认生效了?
4.1 启动日志怎么看?
看这几条关键日志:
INFO o.s.b.a.e.web.EndpointLinksResolver : Exposing 3 endpoint(s) beneath base path '/actuator'
INFO o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http)
INFO o.s.b.a.e.mvc.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/metrics],methods=[GET]}" onto ...
如果有这些输出,说明:
- 成功注册了
/actuator/metrics; - 使用的是 v3 协议格式(Spring Boot 3.x 默认);
- 已绑定到具体处理器方法。
如果没有?检查三点:
- 是否真加了
spring-boot-starter-actuator? - 是否被
@SpringBootApplication(scanBasePackages = ...)扫描范围限制? - 是否误加了
spring.autoconfigure.exclude=...排除自动装配?
开启调试日志进一步诊断:
logging:
level:
org.springframework.boot.actuate: DEBUG
4.2 遇到 404?照着这张表一步步查!
| 检查项 | 检查方法 | 解决方案 |
|---|---|---|
| 1. 依赖是否引入 | 查看 pom.xml 或 build.gradle | 添加 starter 依赖 |
| 2. 端点是否启用 | 日志中是否有 Mapping 输出 | 设置 enabled=true |
| 3. 是否暴露 | include 是否包含 metrics | 添加到 exposure.include |
| 4. 路径是否变更 | 是否设置了 path-mapping | 使用新路径访问 |
| 5. 是否启用了独立管理端口 | management.server.port 是否设置 | 改为对应端口访问 |
特别注意:Spring Boot 2.x 和 3.x 默认协议不同。3.x 用 v3 ,2.x 用 v2 。如果客户端不兼容,需配置:
management:
endpoints:
web:
exposure:
versions: ["v3"]
4.3 用 curl 测试接口可达性(终极手段)
最直接的方法:
curl -i http://localhost:8080/actuator/metrics
预期响应头:
HTTP/1.1 200 OK
Content-Type: application/vnd.spring-boot.actuator.v3+json
响应体示例:
{
"names": [
"jvm.memory.max",
"jvm.memory.used",
"system.cpu.count",
"http.server.requests"
]
}
带认证访问:
curl -u admin:admin123 http://localhost:8080/actuator/metrics
还可以查具体指标:
curl http://localhost:8080/actuator/metrics/http.server.requests
返回详细测量值和标签,用于后续分析。
五、内置指标详解:从 HTTP 到 JVM,全面掌握系统状态
Actuator 不只是给你一个 API,它还自带一堆“开箱即用”的监控指标。理解这些指标的含义和结构,是你做性能调优的前提。
5.1 HTTP 请求监控: http.server.requests 指标深度解析
这是最常用、最有价值的指标之一。
它有哪些维度?
| 标签名 | 示例值 | 说明 |
|---|---|---|
| method | GET, POST | 请求方法 |
| uri | /api/users/{id} | 路径模板(非具体参数) |
| status | 200, 500 | 响应状态码 |
| exception | IllegalStateException | 抛出异常类名,无则为 None |
| outcome | SUCCESS, CLIENT_ERROR | 结果分类,基于状态码划分 |
看个 Controller 示例:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
if (id <= 0) throw new IllegalArgumentException("Invalid ID");
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
}
每次请求都会生成这样的时间序列:
http_server_requests_seconds_count{method="GET",uri="/api/users/{id}",status="400",exception="IllegalArgumentException"} → 1
http_server_requests_seconds_sum{method="GET",uri="/api/users/{id}",status="200",exception="None"} → 0.045
其中 _count 是请求数, _sum 是总耗时,平均延迟 = sum / count。
想看 P95/P99?需要开启百分位统计:
management:
metrics:
web:
server:
request:
autotime:
enabled: true
percentiles: 0.95,0.99
percentile-buckets: 0.005,0.01,0.025,0.05,0.1,0.2,0.5,1.0
否则默认不会计算分位数,节省性能开销。
如何定位高延迟或频繁失败的接口?
PromQL 查询帮你搞定。
找出过去 5 分钟平均延迟最高的 Top 3 接口:
topk(3,
sum(rate(http_server_requests_seconds_sum[5m]))
/
sum(rate(http_server_requests_seconds_count[5m]))
by (uri)
)
检测高频 5xx 错误:
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (uri, method)
> 1 # 每秒超过一次
结合 Grafana,你可以做出“Top N 慢接口”面板,实时告警。
还想查异常激增?试试这个:
sum(increase(http_server_requests_seconds_count{exception!="None"}[1h])) by (exception)
如果 NullPointerException 突增,赶紧去查代码有没有漏判空!
整个请求生命周期的数据流动如下:
flowchart TD
A[HTTP Request Arrives] --> B{DispatcherServlet}
B --> C[Micrometer HandlerInterceptor]
C --> D[Start Timer & Capture Tags]
D --> E[Execute Controller Logic]
E --> F{Exception Thrown?}
F -- Yes --> G[Record Exception Tag]
F -- No --> H[Set exception=None]
G & H --> I[Stop Timer, Emit Metrics]
I --> J[Store in MeterRegistry]
J --> K[Exposed via /actuator/metrics]
K --> L[Scraped by Prometheus]
L --> M[Grafana Visualization]
Micrometer 通过拦截器织入 Spring MVC 生命周期,在请求开始和结束时采集数据,完美无侵入。
能不能关联 trace ID 实现链路追踪?
可以!但要小心“时间序列爆炸”。
Sleuth 会自动注入 traceId 到 MDC。我们可以自定义标签提供者:
@Component
public class CustomWebMvcTagsProvider implements WebMvcTagsProvider {
@Override
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response,
Object handler, Throwable ex) {
String traceId = Span.current().getSpanContext().getTraceId();
return Tags.of(
WebMvcTags.method(request),
WebMvcTags.uri(request, response, handler),
WebMvcTags.status(response),
Tag.of("trace_id", traceId != null ? traceId : "unknown")
);
}
}
⚠️ 注意:每个唯一 trace_id 都会产生新的时间序列,基数太高会压垮 Prometheus。
建议做法:
- 日常监控 不要开启 ;
- 调试时临时采样上报;
- 或将
trace_id写入日志,通过 ELK 联合查询。
5.2 JVM 层面监控:内存、GC、线程一把抓
JVM 是 Java 应用的命脉。了解它的状态,等于掌握了系统的“心跳”。
内存使用情况: jvm.memory.used vs max
| 指标名称 | 类型 | 单位 | 描述 |
|---|---|---|---|
| jvm_memory_used{area,id} | Gauge | bytes | 当前已使用内存 |
| jvm_memory_max{area,id} | Gauge | bytes | 最大可用内存(-1 表示无限) |
| jvm_memory_committed{id} | Gauge | bytes | 已提交给 JVM 的物理内存 |
标签说明:
-
area:heap(堆) ornon-heap(非堆) -
id: 内存池名称,如PS Eden Space,Metaspace
例如:
jvm_memory_used{area="heap", id="PS Old Gen"} → 450MB
jvm_memory_max{area="heap", id="PS Old Gen"} → 1GB
计算老年代使用率:
jvm_memory_used{area="heap", id="PS Old Gen"}
/
jvm_memory_max{area="heap", id="PS Old Gen"}
持续接近 1.0?警惕内存泄漏!
主要内存区域对比:
| 区域 | 所属 area | 用途 | 监控建议 |
|---|---|---|---|
| PS Eden Space | heap | 新生代对象存放区 | 关注 YGC 频率 |
| PS Survivor Space | heap | 存活对象过渡区 | 一般较小 |
| PS Old Gen | heap | 长期存活对象 | 重点关注使用率 |
| Metaspace | non-heap | 类元数据 | 动态扩展,防溢出 |
| Compressed Class Space | non-heap | 压缩类指针 | 通常无需关注 |
合理设置 -Xms , -Xmx , -XX:MaxMetaspaceSize 是前提。
GC 暂停监控: jvm.gc.pause 指标解读
GC 直接影响用户体验。 jvm.gc.pause 是一个 Timer 指标,记录每次 GC 的耗时和次数。
标签包括:
-
action:end of minor GCorend of major GC -
cause: 触发原因,如Allocation Failure -
statistic:count(次数) ortotal(累计时间)
示例数据:
jvm_gc_pause_seconds_count{action="end of minor GC"} → 120
jvm_gc_pause_seconds_total{action="end of major GC"} → 1.8
常用查询:
# 每分钟 Full GC 次数
rate(jvm_gc_pause_seconds_count{action="end of major GC"}[1m])
# 平均 Minor GC 暂停时间
avg(rate(jvm_gc_pause_seconds_total[1m])) / avg(rate(jvm_gc_pause_seconds_count[1m]))
理想情况:
- Minor GC 快速完成(< 50ms),频率可高;
- Major GC 尽量少(每天几次以内),否则可能影响 SLA。
Grafana 曲线图能清晰展示 GC 波动,帮助识别周期性长时间暂停。
线程监控与死锁预警
线程太多会导致上下文切换开销增大,甚至 OOM。
关键指标:
-
jvm_threads_live: 当前活跃线程数 -
jvm_threads_daemon: 守护线程数 -
jvm_threads_peak: 历史峰值 -
jvm_threads_states: 按状态分类(runnable, blocked, waiting)
监控线程增长:
jvm_threads_live > 200
虽然 Micrometer 不直接暴露死锁信息,但我们能自己补上:
@Scheduled(fixedDelay = 60_000)
public void checkDeadlock() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadBean.findDeadlockedThreads();
if (deadlockedThreads != null && deadlockedThreads.length > 0) {
meterRegistry.counter("jvm_threads_deadlock_detected").increment();
log.warn("Deadlock detected among {} threads", deadlockedThreads.length);
}
}
定时任务每分钟检测一次,并以上报指标形式集成进告警系统。
可视化线程状态分布:
pie
title JVM Threads State Distribution
“Runnable” : 45
“Blocked” : 10
“Waiting” : 30
“Timed Waiting” : 15
饼图有助于判断是否存在大量线程阻塞。
5.3 系统与运行环境指标:跳出 JVM 看全局
除了 JVM 内部,系统级指标也很重要。
system.cpu.usage vs process.cpu.usage ,有何区别?
| 指标名称 | 数据源 | 含义 | 单位 |
|---|---|---|---|
| system.cpu.usage | 操作系统 | 主机整体 CPU 使用率 | 0~1 |
| process.cpu.usage | JVM 采样 | 当前进程占用 CPU 时间比 | float |
关键差异:
-
system.cpu.usage是瞬时值; -
process.cpu.usage是累计增量,需两次采样才能得出比率。
启用后者需配置:
management:
metrics:
enable:
process: true
然后查询:
rate(process_cpu_usage_seconds_total[15s])
若结果为 1.2,表示该进程占用了 1.2 个 CPU 核心。
对比二者可判断:
(system_cpu_usage > 0.8) and (rate(process_cpu_usage_seconds_total[1m]) < 0.1)
主机忙但应用闲?可能是其他进程在抢资源!
文件描述符监控:防止 Too Many Open Files
Linux 对每个进程打开的文件数有限制。 file.descriptor.count 帮你提前发现问题。
相关指标:
-
process_files_opened: 自启动以来打开过的总文件数(Counter) -
process_files_max: 允许的最大文件数(Gauge)
计算使用率:
process_files_opened / process_files_max
超过 80%?可能出现“Too many open files”错误。
解决方案:
- 调整
ulimit -n - 优化连接池配置(如 DB、Redis)
服务生命周期监控:uptime 和 startup.time
最后两个重要指标:
-
application_uptime_seconds: 应用已运行时间 -
application_startup_duration_seconds: 启动耗时
它们反映服务稳定性与部署效率。
监控重启次数:
changes(application_uptime_seconds[1h]) > 100
频繁重启?可能是健康检查失败、OOM 或滚动发布引起。
同时, startup.time 帮助发现初始化缓慢的 Bean,推动冷启动优化。
六、编程访问 metrics 数据:不只是看,还要能处理
你以为 /actuator/metrics 只是用来给人看的?错!它也可以被程序消费。
6.1 获取指标列表与详情
先查有哪些指标:
curl http://localhost:8080/actuator/metrics
响应:
{
"names": [
"jvm.memory.max",
"jvm.memory.used",
"http.server.requests"
]
}
再查具体指标:
curl "http://localhost:8080/actuator/metrics/http.server.requests"
响应结构:
{
"name": "http.server.requests",
"description": "Time taken to handle requests",
"baseUnit": "seconds",
"measurements": [
{ "statistic": "COUNT", "value": 156.0 },
{ "statistic": "TOTAL_TIME", "value": 7.89 },
{ "statistic": "MAX", "value": 0.15 }
],
"availableTags": [
{ "tag": "method", "values": ["GET", "POST"] },
{ "tag": "uri", "values": ["/api/user", "/login"] }
]
}
measurements 包含 COUNT、SUM、MAX; availableTags 提供多维切片能力。
Java 客户端示例:
public class MetricsClient {
private final RestTemplate restTemplate = new RestTemplate();
public void fetchMetricDetails(String metricName) {
String url = "http://localhost:8080/actuator/metrics/" + metricName;
Map<String, Object> response = restTemplate.getForObject(url, Map.class);
System.out.println("Metric Name: " + response.get("name"));
List<Map<String, Object>> measurements = (List<Map<String, Object>>) response.get("measurements");
for (Map<String, Object> m : measurements) {
System.out.printf(" %s = %.3f%n", m.get("statistic"), m.get("value"));
}
List<Map<String, Object>> tags = (List<Map<String, Object>>) response.get("availableTags");
System.out.println("Available Tags:");
for (Map<String, Object> t : tags) {
List<String> values = (List<String>) t.get("values");
System.out.printf(" %s -> %s%n", t.get("tag"), String.join(", ", values));
}
}
}
可用于构建本地采样器、巡检脚本或简易告警引擎。
6.2 多维标签的实际意义:灵活聚合与钻取
Micrometer 的灵魂在于 多维标签 。
传统方式要维护一堆指标:
-
http_get_200_count -
http_post_500_count - …
而 Micrometer 用一个 http.server.requests 加标签搞定:
{
"method": "GET",
"uri": "/api/user",
"status": "200",
"count": 80,
"avg_time": 0.045
}
想按状态码统计失败率?分组聚合就行!
PromQL 示例:
sum by(status) (rate(http_server_requests_seconds_count[5m]))
Grafana 饼图直观展示:
pie
title HTTP Status Distribution
“200 OK” : 150
“404 Not Found” : 8
“500 Server Error” : 6
还能动态传参过滤:
curl "http://localhost:8080/actuator/metrics/http.server.requests?tag=status:500&tag=uri:/api/order"
精准锁定问题接口。
6.3 编程方式实现本地监控与告警
封装一个采样器:
@Component
public class LocalMetricsSampler {
@Autowired
private MeterRegistry meterRegistry;
@Scheduled(fixedRate = 30000)
public void sampleHttpPerformance() {
Map<String, Object> data = queryMetricByTags("http.server.requests",
Collections.singletonMap("uri", "/api/user"));
Double total = extractValue(data, "TOTAL_TIME");
Double count = extractValue(data, "COUNT");
if (count > 0 && total / count > 0.1) {
triggerAlert("High latency on /api/user: " + (total/count*1000) + " ms");
}
}
private void triggerAlert(String msg) {
log.warn("ALERT: {}", msg);
// 发邮件、调 webhook...
}
}
虽简单,但已具备基本闭环能力,适用于中小系统。
七、接入 Prometheus:打造真正的生产级监控体系
前面都是准备,这才是重头戏。
7.1 引入 micrometer-registry-prometheus
Maven:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Gradle:
implementation 'io.micrometer:micrometer-registry-prometheus'
引入后,Spring Boot 会自动注册 PrometheusMeterRegistry ,并通过条件装配启用 /actuator/prometheus 端点。
确保暴露它:
management:
endpoints:
web:
exposure:
include: prometheus
7.2 Prometheus 抓取配置
编辑 prometheus.yml :
scrape_configs:
- job_name: 'spring-boot-app'
scrape_interval: 10s
scrape_timeout: 5s
static_configs:
- targets: ['localhost:8080']
labels:
env: dev
team: backend
重启 Prometheus,访问 UI → Status → Targets,看到 UP 状态才算成功。
测试查询:
http_server_requests_seconds_count
应该能看到数据。
7.3 性能调优与陷阱规避
- 启用 gzip 压缩 :减少传输体积;
- 限制标签基数 :防 cardinality explosion;
- 聚合细粒度 URI :避免
/user/1,/user/2成千上万; - 使用 recording rules :预计算常用表达式,提升查询速度;
- 慎用 Pushgateway :只适合短任务,不适合长期服务。
八、Grafana 可视化:让数据说话
最后一步,把数据变成看得懂的图表。
8.1 配置 Prometheus 数据源
Grafana → Configuration → Data Sources → Add → Prometheus
填入 URL,点击 Save & Test。
8.2 构建核心仪表板
JVM 内存趋势图
100 * jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}
单位:Percent,设置告警阈值 80%
HTTP QPS 与 P95 延迟
QPS:
rate(http_server_requests_seconds_count[1m])
P95:
histogram_quantile(0.95, sum by(le) (rate(http_server_requests_seconds_bucket[1m])))
Bar Gauge 显示线程数与 GC 次数
-
jvm_threads_live_threads -
increase(jvm_gc_pause_seconds_count[5m])
8.3 告警闭环流程
flowchart TD
A[Spring Boot App] --> B[Actuator]
B --> C[Prometheus]
C --> D[Grafana]
D --> E[Alert]
E --> F[通知 SRE]
F --> G[修复]
G --> A
形成完整可观测性闭环。
🎯 结语 :
这一套组合拳打下来,你的应用不再是“黑盒”,而是透明可控的系统。
从一行配置开始,到一整套监控体系落地,你已经掌握了现代 Java 工程师的核心技能之一。
现在,就去给你的服务加上 /actuator/metrics 吧!🚀
简介:《Spring Boot Metrics:深入理解与应用》介绍了Spring Boot框架中的核心组件spring-boot-metrics,它通过集成Actuator实现对应用程序运行时指标的自动采集与监控。该工具可收集HTTP请求、JVM内存、线程状态、数据库查询等关键性能数据,并提供标准化端点如/actuator/metrics和/actuator/prometheus供外部系统读取。支持与Prometheus、Grafana等主流监控平台无缝集成,实现指标可视化。开发者还可通过@Timed、@Counted等注解及MeterRegistry接口自定义业务级度量指标,全面提升系统的可观测性与可维护性。本项目经过完整测试,适用于开发、测试与生产环境下的性能分析与故障排查。
Spring Boot性能监控实战
1364

被折叠的 条评论
为什么被折叠?



