资讯动态

Spring Boot Actuator 服务端性能监控最佳实践与核心端点详解

发布时间:2026/10/12 22:55:01 来源:尧图企业网站定制
1. 为什么服务端性能监控绕不开 Spring Boot Actuator做服务端开发的朋友大概率都经历过这样的场景线上接口突然变慢用户投诉电话打过来你登录服务器想看看到底是哪里出了问题结果发现除了日志文件手头没有任何实时数据可以参考。CPU 多少、内存用了多少、数据库连接池是不是满了、有多少线程在阻塞——全靠猜。这种“盲人摸象”式的排查体验相信每个后端人都深有体会。我最早做监控的时候走的是“自己动手”的路线写个定时任务定时采集系统指标再写个接口暴露出去最后对接监控平台。这套方案能用但维护成本极高每个项目都要复制一遍改一处就得全量更新。后来接触到 Spring Boot Actuator才发现原来官方早就把这件事想明白了——它提供了一套开箱即用的生产级监控能力你只需要加一个依赖、改几行配置就能拿到应用内部几乎所有的运行时状态。这篇文章要聊的就是 Spring Boot Actuator 在服务端性能监控场景下的最佳实践。我会从它的设计思路讲起拆解核心端点的用途和配置方式再结合我实际项目中的落地经验把那些文档里不会写的坑和技巧一并分享出来。无论你是刚接触服务端监控的新手还是已经在用 Actuator 但只停留在/health层面的老手相信都能从中找到一些之前忽略的细节。整篇内容会围绕“怎么用、为什么这么用、用了之后怎么排查问题”这条主线展开力求让你看完就能在自己的项目里复现。2. Actuator 的整体设计与核心思路拆解2.1 它到底解决了什么问题在没有 Actuator 之前一个 Spring Boot 应用对外基本是个黑盒。你知道它在跑但不知道它跑得好不好。想了解内部状态要么自己写端点要么依赖外部工具去“猜”。Actuator 的核心价值就是把应用内部的运行时信息通过 HTTP 或 JMX 暴露出来让运维和开发人员能够实时观测。它的设计哲学可以用四个字概括约定优于配置。你不需要定义任何接口只要引入spring-boot-starter-actuator依赖它就会自动注册一系列端点Endpoint每个端点负责一类信息的采集和暴露。比如/health看健康状态/metrics看指标数据/env看环境变量/threaddump看线程快照。这种“端点即功能”的设计让监控能力的接入成本降到了最低。从架构上看Actuator 分为两层端点层和暴露层。端点层负责采集数据暴露层负责决定通过什么协议、以什么方式对外提供。默认情况下只有/health和/info是通过 HTTP 暴露的其余端点需要通过配置手动开启。这个设计很关键——它避免了敏感信息被无意中暴露出去是一种安全优先的默认策略。2.2 端点分类与选型逻辑Actuator 提供的端点有几十个但常用的其实就那么十来个。我习惯把它们分成三类第一类是健康检查类核心是/health。它不仅能告诉你应用是 UP 还是 DOWN还能聚合数据库、Redis、磁盘空间、消息队列等组件的健康状态。这个端点通常对接负载均衡或容器编排平台用来决定是否把流量转发到该实例。第二类是指标采集类核心是/metrics。它暴露的是数值型数据比如 JVM 内存使用量、GC 次数、HTTP 请求耗时分布、数据库连接池活跃数等。这些数据是性能监控的核心原料通常会被 Prometheus 之类的系统定期抓取。第三类是诊断排查类包括/threaddump、/heapdump、/env、/configprops、/loggers等。这类端点在出问题时特别有用比如线程池满了可以看线程快照配置不对可以看环境变量日志级别需要临时调整可以直接通过/loggers动态修改。选型的时候有个原则按需暴露最小权限。生产环境不要图省事把management.endpoints.web.exposure.include设成*而是明确列出需要的端点。我见过太多项目因为暴露了/env或/heapdump导致敏感信息泄露的案例这个后面会详细说。2.3 与主流监控体系的衔接方式Actuator 本身不是一套完整的监控系统它更像是一个“数据源”。它负责把应用内部的状态暴露出来至于怎么存储、怎么展示、怎么告警那是 Prometheus、Grafana、ELK 这些系统的事。目前最主流的搭配是Actuator Micrometer Prometheus Grafana。Micrometer 是 Spring Boot 2.x 之后引入的指标门面Actuator 的/metrics端点底层就是通过 Micrometer 采集的。Micrometer 支持多种监控系统作为后端你只需要引入对应的 registry 依赖指标就会自动以该系统的格式暴露。比如引入micrometer-registry-prometheus后/actuator/prometheus端点就会输出 Prometheus 格式的指标文本Prometheus 直接抓取即可。这种分层设计的好处是解耦。应用代码只依赖 Micrometer 的 API不关心后端是 Prometheus 还是别的什么。将来要换监控系统只需要换一个依赖和几行配置业务代码完全不用动。这也是我在技术选型时特别看重的一点——基础设施的替换成本要低。3. 核心端点详解与配置实操3.1 健康检查端点不只是 UP 和 DOWN/health是 Actuator 最常用的端点但很多人只把它当成一个“存活探针”这就浪费了它一半的价值。默认情况下它会聚合一系列HealthIndicator的结果包括磁盘空间、数据库、Redis、Elasticsearch 等取决于你引入了哪些依赖。访问/actuator/health默认只返回{status:UP}不展示细节。要看每个组件的具体状态需要配置management: endpoint: health: show-details: always配置后返回的内容会变成这样{ status: UP, components: { db: { status: UP, details: { database: MySQL, validationQuery: isValid() } }, diskSpace: { status: UP, details: { total: 499963174912, free: 367656960000, threshold: 10485760 } }, redis: { status: UP, details: { version: 6.2.6 } } } }这里有个细节值得注意show-details有三个可选值——never、when-authorized、always。生产环境我建议用when-authorized配合 Spring Security 做权限控制只有特定角色的用户才能看到详细信息。直接设成always虽然方便但会把数据库类型、磁盘容量这些信息暴露给所有能访问该端点的人。另外健康检查的聚合策略也值得关注。默认情况下只要有一个组件 DOWN整体状态就是 DOWN。但有些场景下我们希望某个非核心组件的故障不影响整体健康判断这时可以通过management.health.status.order调整状态优先级或者自定义HealthAggregator。我在一个项目里就遇到过 Redis 偶尔抖动导致健康检查频繁失败、容器被反复重启的问题后来把 Redis 的健康检查改成非致命级别才解决。3.2 指标端点性能数据的源头/metrics端点是性能监控的数据核心。它暴露的指标分为几大类JVM 指标内存各区域使用量、GC 次数和耗时、线程数、类加载数系统指标CPU 使用率、文件描述符数、系统负载HTTP 指标各接口的请求次数、响应时间分布、错误率连接池指标数据库连接池的活跃连接数、空闲连接数、等待次数缓存指标缓存命中率、加载耗时访问/actuator/metrics会返回所有可用指标的名称列表。要看具体某个指标的数值需要带上指标名比如/actuator/metrics/jvm.memory.used。返回格式如下{ name: jvm.memory.used, description: The amount of used memory, baseUnit: bytes, measurements: [ { statistic: VALUE, value: 1.23456789E8 } ], availableTags: [ { tag: area, values: [heap, nonheap] }, { tag: id, values: [G1 Eden Space, G1 Old Gen, Metaspace] } ] }这里的关键是availableTags。它告诉你这个指标可以按哪些维度细分。比如jvm.memory.used可以按area堆/非堆和id具体内存区域过滤。要查堆内存的使用量就访问/actuator/metrics/jvm.memory.used?tagarea:heap。不过说实话通过 HTTP 手动查指标只适合调试。真正的生产监控一定是让 Prometheus 定期抓取/actuator/prometheus端点。这个端点会把所有指标以 Prometheus 的文本格式一次性输出抓取效率高得多。配置方式management: endpoints: web: exposure: include: health,info,prometheus,metrics metrics: export: prometheus: enabled: true引入micrometer-registry-prometheus依赖后/actuator/prometheus会自动可用。Prometheus 的抓取配置大概长这样scrape_configs: - job_name: spring-boot-app metrics_path: /actuator/prometheus scrape_interval: 15s static_configs: - targets: [localhost:8080]15 秒的抓取间隔是个比较平衡的选择。太短会增加应用和 Prometheus 的负担太长则可能漏掉一些瞬时峰值。对于核心服务我一般设 10 秒非核心服务 30 秒也够用。3.3 诊断端点出问题时的救命稻草当线上出现性能问题时/threaddump、/heapdump、/loggers这几个端点往往能救命。/threaddump返回当前 JVM 所有线程的快照包括线程名、状态、调用栈。当发现接口响应慢时连续抓几次线程快照对比一下就能定位到阻塞点。比如你发现大量线程卡在WAITING状态调用栈指向某个数据库连接获取操作那基本可以确定是连接池不够用了。/heapdump会生成堆转储文件用于分析内存泄漏。但这个端点要慎用——生成 heapdump 会触发 Full GC期间应用会暂停大堆内存下可能暂停好几秒。生产环境一定要加权限控制而且不要在高峰期手动触发。/loggers端点支持动态修改日志级别这个功能在排查问题时特别实用。比如某个接口偶发异常但日志级别是 INFO 看不到细节你可以直接发个 POST 请求把对应包的日志级别调到 DEBUGcurl -X POST http://localhost:8080/actuator/loggers/com.example.service \ -H Content-Type: application/json \ -d {configuredLevel:DEBUG}排查完再调回去全程不用重启应用。这个端点我强烈建议在生产环境开启但同样要加权限控制。3.4 端点暴露的安全配置前面反复提到权限控制这里具体说说怎么做。最直接的方式是引入 Spring Security然后对/actuator/**路径做拦截Configuration public class ActuatorSecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth - auth .requestMatchers(/actuator/health).permitAll() .requestMatchers(/actuator/**).hasRole(ADMIN) .anyRequest().authenticated() ); return http.build(); } }这样/health对外公开供负载均衡探活其余端点需要 ADMIN 角色才能访问。如果不想引入完整的 Spring Security也可以自定义一个Filter做简单的 Token 校验但安全性不如前者。还有一个容易被忽略的点管理端口的分离。Actuator 端点默认和应用业务端口共用同一个 HTTP 端口。更好的做法是让管理端点走独立端口management: server: port: 8081这样你可以只在管理端口上做访问控制业务端口完全不受影响。在容器化环境中管理端口通常不对外暴露只允许监控系统在内网访问安全性更高。4. 完整实操流程与关键环节实现4.1 依赖引入与基础配置先从一个空白的 Spring Boot 项目开始。假设你用的是 Maven在pom.xml中加入以下依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency dependency groupIdio.micrometer/groupId artifactIdmicrometer-registry-prometheus/artifactId /dependency第一个是 Actuator 核心依赖第二个是 Prometheus 的指标注册器。如果你用的是 Gradle对应写法是implementation org.springframework.boot:spring-boot-starter-actuator implementation io.micrometer:micrometer-registry-prometheus接下来是application.yml的配置。我把它分成三块端点暴露、指标配置、健康检查。management: server: port: 8081 endpoints: web: exposure: include: health,info,metrics,prometheus,loggers,threaddump base-path: /manage endpoint: health: show-details: when-authorized probes: enabled: true metrics: enabled: true metrics: tags: application: ${spring.application.name} distribution: percentiles-histogram: http.server.requests: true slo: http.server.requests: 50ms,100ms,200ms,500ms,1s逐项解释一下。management.server.port: 8081让管理端点走独立端口。base-path: /manage把默认的/actuator前缀改成/manage这算是一个轻量的安全措施——攻击者不知道路径就无从下手。show-details: when-authorized配合权限控制展示健康详情。probes.enabled: true开启 Kubernetes 的存活和就绪探针支持会额外暴露/manage/health/liveness和/manage/health/readiness两个端点。metrics.tags.application给所有指标打上应用名标签这在多应用共用一套 Prometheus 时非常关键否则你根本分不清指标来自哪个服务。percentiles-histogram和slo是 HTTP 请求指标的进阶配置前者生成百分位直方图后者定义 SLO 分桶两者结合可以精确计算 P95、P99 响应时间。4.2 自定义业务指标埋点Actuator 自带的指标覆盖了 JVM 和 HTTP 层面但业务层面的指标需要自己埋。Micrometer 提供了MeterRegistry注入后就能创建各种类型的指标。最常用的是Counter计数器和Timer计时器。比如统计订单创建次数和耗时Service public class OrderService { private final Counter orderCreatedCounter; private final Timer orderCreateTimer; public OrderService(MeterRegistry registry) { this.orderCreatedCounter Counter.builder(business.order.created) .description(Number of orders created) .tag(type, normal) .register(registry); this.orderCreateTimer Timer.builder(business.order.create.duration) .description(Order creation duration) .publishPercentiles(0.5, 0.95, 0.99) .register(registry); } public Order createOrder(OrderRequest request) { return orderCreateTimer.record(() - { Order order doCreateOrder(request); orderCreatedCounter.increment(); return order; }); } }这里有几个经验点。第一指标命名用点分隔的层级结构比如business.order.created这样在 Prometheus 里查询时可以用前缀匹配。第二Timer.record()接受一个Supplier自动计算耗时比手动记开始结束时间更简洁也不容易出错。第三publishPercentiles会生成 P50、P95、P99 指标这些比平均值更能反映真实用户体验——平均值会被大量快请求拉低掩盖长尾问题。还有一个容易踩的坑标签的基数问题。给指标打标签时标签值的取值范围必须有限。比如用用户 ID 做标签如果有十万用户就会产生十万条时间序列直接把 Prometheus 打爆。正确的做法是用用户等级、地区这类有限枚举值做标签。我见过一个项目用请求 URL 做标签结果因为 URL 里带了参数导致指标爆炸这个教训值得记住。4.3 Prometheus 抓取与 Grafana 展示应用侧配置好后接下来是监控侧的搭建。Prometheus 的配置文件prometheus.yml核心就是抓取任务global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: - job_name: spring-boot metrics_path: /manage/prometheus static_configs: - targets: - app-server-1:8081 - app-server-2:8081 labels: env: production注意metrics_path要和我们前面配置的base-path对应上。labels里可以加环境标签方便区分生产和测试。Grafana 这边我一般会导入现成的 Spring Boot 仪表盘模板然后根据业务需求做定制。核心面板包括面板名称数据来源关注点JVM 内存使用jvm.memory.used堆内存增长趋势是否有泄漏GC 暂停时间jvm.gc.pause每次 GC 耗时频率是否异常HTTP 请求 QPShttp.server.requests流量变化突增突降接口响应时间 P99http.server.requests长尾延迟用户体验数据库连接池hikaricp.connections.active连接是否耗尽线程数jvm.threads.live线程是否持续增长这些面板组合起来基本能覆盖 80% 的性能问题排查场景。我习惯把 JVM 内存和 GC 面板放在最上面因为内存问题是最常见的性能杀手。然后是 HTTP 指标最后是连接池和线程。每个面板都设置告警阈值比如 P99 超过 1 秒、堆内存使用率超过 85% 就触发告警。4.4 告警规则配置光有展示不够还得有告警。Prometheus 的告警规则写在单独的文件里groups: - name: spring-boot-alerts rules: - alert: HighHttpLatency expr: histogram_quantile(0.99, rate(http_server_requests_seconds_bucket[5m])) 1 for: 5m labels: severity: warning annotations: summary: 接口 P99 延迟超过 1 秒 description: {{ $labels.uri }} 的 P99 延迟为 {{ $value }}s - alert: HighHeapUsage expr: jvm_memory_used_bytes{areaheap} / jvm_memory_max_bytes{areaheap} 0.85 for: 10m labels: severity: critical annotations: summary: 堆内存使用率超过 85% - alert: DbConnectionPoolExhausted expr: hikaricp_connections_active / hikaricp_connections_max 0.9 for: 2m labels: severity: critical annotations: summary: 数据库连接池接近耗尽告警规则的设计有两个要点。一是for字段表示条件持续多久才触发告警。设得太短会被瞬时抖动误报设得太长又会延迟发现真问题。我一般对延迟类告警设 5 分钟对资源耗尽类设 2 分钟。二是告警分级warning 级别发到群里critical 级别直接打电话。分级能避免告警疲劳——如果所有告警都一个级别大家很快就会麻木。5. 常见问题与排查技巧实录5.1 端点访问返回 404这是新手最常遇到的问题。引入依赖后访问/actuator/health却返回 404原因通常有三个。第一端点没暴露。Spring Boot 2.x 默认只暴露/health和/info其他端点需要在management.endpoints.web.exposure.include里显式列出。注意这个配置是覆盖式的一旦你写了include: health那/info也会被关掉。第二路径不对。如果你配置了base-path或management.server.port访问路径要相应调整。比如配了独立端口 8081 和 base-path/manage正确路径是http://localhost:8081/manage/health。第三Spring Security 拦截。如果引入了 Spring Security 但没放行 Actuator 路径请求会被重定向到登录页或返回 401。检查一下安全配置里有没有对/actuator/**或你自定义的 base-path 做放行。排查的时候有个小技巧启动应用时打开 DEBUG 日志搜索Exposing关键字会看到类似这样的输出Exposing 5 endpoint(s) beneath base path /actuator这能直接告诉你实际暴露了哪些端点、base path 是什么。5.2 指标数据缺失或不更新有时候/actuator/prometheus能访问但某些指标就是没有。常见原因有这几个。依赖缺失。比如想看数据库连接池指标但用的是 Druid 而不是 HikariCP那hikaricp.*指标自然不存在。Druid 需要额外引入druid-spring-boot-starter并配置spring.datasource.druid.stat-view-servlet相关参数。指标未注册。Micrometer 的很多指标是懒加载的只有第一次被访问时才会注册。比如 HTTP 请求指标在应用刚启动、还没有任何请求时是查不到的。发几个请求再查就出现了。标签过滤错误。查指标时带了不存在的标签值会返回空结果。比如jvm.memory.used?tagarea:heap是对的但写成tagarea:Heap大小写错误就查不到。建议先不带标签查一次从availableTags里确认正确的标签值。5.3 健康检查导致容器频繁重启这个问题在 Kubernetes 环境特别常见。健康检查端点响应慢或者偶尔失败K8s 就认为 Pod 不健康触发重启重启后问题依旧形成恶性循环。根因通常是健康检查里包含了不稳定的外部依赖。比如数据库偶尔网络抖动/health就返回 DOWNK8s 就把 Pod 杀了。但数据库抖动是暂时的杀 Pod 根本解决不了问题反而让服务更不稳定。解决办法是区分存活探针和就绪探针。存活探针只检查应用进程本身是否正常不检查外部依赖就绪探针才检查依赖是否可用不可用时只是把 Pod 从负载均衡里摘掉不重启。Actuator 的probes.enabled: true就是干这个的management: endpoint: health: probes: enabled: true group: liveness: include: livenessState readiness: include: readinessState,db,redis这样/health/liveness只反映应用自身状态/health/readiness才包含数据库和 Redis。K8s 的livenessProbe指向前者readinessProbe指向后者问题就解决了。5.4 常见问题速查表问题现象可能原因排查方法解决方案端点 404未暴露/路径错/被拦截看启动日志 Exposing 行检查 exposure 配置和 base-path指标缺失依赖缺失/懒加载/标签错查/metrics列表补依赖、发请求触发、核对标签健康检查误报外部依赖抖动看/health详情拆分存活/就绪探针指标基数爆炸标签值过多看 Prometheus 序列数改用有限枚举值做标签heapdump 卡顿Full GC 暂停看 GC 日志低峰期操作加权限控制管理端口冲突端口被占用看启动报错换端口或复用主端口5.5 几个踩坑心得关于show-details生产环境千万别图省事设成always。我见过一个项目把健康详情完全暴露结果外部扫描器直接拿到了数据库地址和版本号。用when-authorized配合 Spring Security多写几行配置安全系数高很多。关于指标命名自定义指标一定要加业务前缀比如business.、order.避免和框架自带指标冲突。同时命名要稳定一旦上线就不要改否则历史数据就断了。我一般会在代码里用常量定义指标名而不是散落在各处写字符串。关于告警阈值不要照搬网上的默认值。每个应用的正常水位不一样有的应用堆内存常年 70%有的 40% 就算高。上线后先观察一周根据实际基线来设阈值。告警宁少勿滥每条告警都应该是可行动的否则就是噪音。关于管理端口容器化部署时管理端口不要映射到宿主机只在容器网络内可达即可。Prometheus 通过服务发现直接抓取 Pod IP 的管理端口既安全又方便。关于版本兼容Spring Boot 2.x 和 3.x 在 Actuator 配置上有一些差异比如 3.x 对management.endpoints.web.exposure.include的处理更严格。升级版本时一定要回归测试监控功能别等出问题了才发现端点访问不了。6. 从单机监控到体系化观测的延伸Actuator 解决的是单应用内部状态的暴露问题但真实的生产环境往往是几十上百个服务实例。这时候光有 Actuator 不够还需要考虑几个延伸方向。链路追踪。Actuator 的指标是聚合数据看不到单个请求的完整路径。要定位跨服务的性能瓶颈需要引入链路追踪把一次请求经过的所有服务串联起来。Micrometer Tracing 可以和 Actuator 共用同一套标签体系实现指标和链路的关联。日志聚合。Actuator 的/loggers能动态调级别但日志本身还是散落在各台机器上。配合 ELK 或 Loki 做集中式日志收集才能在海量日志中快速检索。容量规划。Actuator 采集的历史指标是容量规划的重要依据。通过分析内存增长趋势、QPS 变化曲线可以预测什么时候需要扩容。我习惯每月做一次指标回顾看看哪些资源的使用率在持续上升提前做扩容准备。混沌工程。有了完善的监控才能安全地做混沌实验。比如主动杀掉一个实例观察健康检查是否及时摘除、流量是否自动转移、告警是否触发。这些验证能让你对系统的容错能力有真实的信心而不是停留在理论层面。这套体系搭起来之后服务端性能监控才算真正落地。Actuator 是其中的基石它把应用内部的状态变得可观测后续的所有分析和决策都建立在这些数据之上。我在实际项目中的体会是监控这件事投入越早收益越大等到出问题才想起来补往往要付出更大的代价。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑