☰
Spring Boot Actuator实战指南:从健康检查到生产监控
2026/10/21 14:15:53 网站建设 项目流程

很多开发者把 Spring Boot 用得飞起,但一直没认真碰过它的 Actuator 模块。我是在一次联调阶段被"服务进程还活着但功能就是异常"的问题卡了两天,才开始静下心把 SpringBoot Actuator 里里外外吃了一遍。这篇笔记我尽量按自己的实际使用顺序来写,先讲清楚它能解决什么,再逐个拆解常用端点,然后说健康检查与自定义指标怎么做,最后把我在生产环境里踩过的坑和一份能直接抄的配置基线放出来。内容对后端开发、测试和运维都适用,偏实践,希望能帮你少走弯路。

1. 为什么我到了第58天才认真看 Actuator:先聊聊它解决的真实痛点

1.1 那次让我改变观念的故障排查

事情发生在一个内部管理系统联调阶段。前端同事反馈登录接口偶发失败,我第一反应是去翻应用日志,结果日志里只有几行不痛不痒的超时记录,进程还在监听端口,docker ps 看状态也是 Up,控制台也没有报错。看起来一切正常,但就是偶发 5xx。后来无意间调了一次/actuator/health,发现返回的 status 不是简单的 UP,里面带着 Redis 连接异常的健康项,才意识到问题根本不在接口代码层,而是共享 Redis 连接不稳定导致部分请求拿不到会话数据。

这次排查给我最大的冲击是:进程存活不等于应用健康。端口通、进程在,只能说明操作系统层面它还活着,但数据库连接池满没满、消息队列通不通、外部依赖是否超时,这些信息在传统"看日志、看端口"的排查链路里是看不见的。Actuator 补上的正是这一块——把应用内部的运行状态通过 HTTP 端点暴露出来,让你能像看仪表盘一样快速判断"这服务现在到底还行不行"。

1.2 Actuator 的定位:不是监控系统,而是监控系统的数据来源

刚开始接触时容易误解,以为 Actuator 就是监控平台。其实它不做数据展示,不负责告警,也不保存历史数据,它只是把应用运行时信息"通过端点暴露"出来:健康状况、环境配置、JVM 指标、日志级别、线程快照、堆内存快照等。

做一个类比:Actuator 相当于汽车仪表盘后面的传感器,它负责采集和输出数据,但真正显示给司机看的是仪表盘,也就是监控平台。生产环境下,你大可以把这些端点接入 Prometheus、Grafana、Spring Boot Admin,或者各种厂商的 APM,让平台帮你画曲线、发告警。但在小型项目和联调环境里,直接 curl 端点也完全够用。

1.3 什么样的人适合读这篇

如果你是刚开始用 Spring Boot 的开发者,这篇能帮你从"项目能跑就行"跨越到"知道项目为什么能跑/不能跑";如果你已经在写接口,但每次排查故障都靠到处加日志,这篇可以给你一套更系统的诊断路径;如果你负责运维和部署,Actuator 的健康检查、指标端点、探针配置对你做自动化发布和监控告警非常有参考价值。我尽量把每一步都写清楚,就算你之前只看过application.properties也能照着操作。

2. 三分钟接入:依赖、端点开关与第一个健康检查

2.1 先加依赖,版本号交给 Spring Boot 统一管理

接入 Actuator 的第一步永远是加依赖。Maven 场景下,在pom.xml中引入:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

如果是 Gradle:

dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' }

不需要手动指定版本号,因为版本由 Spring Boot 父级管理统一锁定。它跟你项目里spring-boot-starter-web的版本是一致的,避免出现兼容性问题。这里有个容易忽略的点:如果你的项目 Web 层用的是 WebFlux,依赖也完全一样,只是端点路径的服务方式有细微差异,后面的配置基本互通。

2.2 为什么默认只能访问到 health 端点

依赖加好后直接启动,你访问http://localhost:8080/actuator/health会得到一个最简单的 JSON:

{ "status": "UP" }

这个status就是目前应用的健康状态。可能有人觉得这跟自己判断"进程活着"没什么区别,别急,当项目里引入了数据库、Redis、RabbitMQ 等组件后,这个返回值会自动变成:

{ "status": "DOWN", "components": { "db": { "status": "UP", "details": { ... } }, "redis": { "status": "DOWN", "details": { ... } } } }

那为什么health默认就能访问,而像metrics、env这些端点默认 404?因为 Spring Boot 在 Web 环境下默认只暴露了health端点,JMX 默认暴露的端点要多得多。这个设计是出于安全考虑,避免你一启动应用就把所有内部状态都暴露到网络里。

2.3 打开更多端点,但要注意控制范围

如果你需要看其他端点,在application.yml中显式配置:

management: endpoints: web: exposure: include: health,info,metrics,env,loggers

配置文件里用的是management.endpoints.web.exposure.include,路径前缀默认是/actuator。这里有新手容易踩的坑:如果你写的是management.endpoints.web.expose或者management.endpoint,属性名对不上,配置不会生效。

如果想临时全开,可以写成include: "*",但不建议在生产环境照抄,因为像env、heapdump、shutdown这类端点一旦暴露,风险和运维成本都很大。我自己的原则是:联调环境可以适度放开,生产环境按最小权限暴露。

2.4 验证第一个健康检查

配置好启动后,可以用 curl 快速验证:

curl http://localhost:8080/actuator/health

也可以打开浏览器访问。如果返回的不是预期的健康状态,优先去看控制台启动日志里有没有Mapped "{"[/actuator/health],produces=[application/vnd.spring-boot.actuator.v3+json]..."之类的映射记录,有就说明端点已经注册,问题多半出在访问路径或网络层。

3. 端点逐个拆解:健康检查只是开胃菜

3.1 /actuator/health:探活之外还有细节

先说健康检查这个端点本身。从 Spring Boot 2.3 开始,Actuator 支持了应用可用性探针,也就是 Kubernetes 里常用的 liveness 和 readiness 两个概念:

  • liveness:判断进程是否需要被重启。如果 liveness 失败,K8s 会杀掉容器重新拉起。
  • readiness:判断服务是否已经可以接收流量。如果 readiness 失败,K8s 会把流量摘掉,但不杀容器。

对应端点路径分别是/actuator/health/liveness和/actuator/health/readiness。默认情况下这两个子端点没有开启,需要在配置里打开:

management: endpoint: health: probes: enabled: true

启用后,容器编排系统就能直接用这两个端点做探针。另外,health端点的返回数据里还经常看到components字段,这就是各类健康检查器的合集。比如你配了 MySQL,就有 DataSource 健康检查;配了 Redis,就有 Redis 健康检查。这些健康检查器还会暴露连接信息,后面我会专门讲自定义健康检查。

3.2 /actuator/info:把构建信息与应用说明放上去

info端点默认返回空对象{},因为它本身不采集任何数据,等你往里塞信息。最常用的是把应用的基本介绍放进去:

info.app.name=内部订单系统 info.app.version=2.1.0 info.app.description=订单查询与处理服务

然后访问/actuator/info:

{ "app": { "name": "内部订单系统", "version": "2.1.0", "description": "订单查询与处理服务" } }

也可以用构建期生成的META-INF/build-info.properties,让 version 字段自动跟随构建产物。info端点适合放非敏感、又希望运维第一时间能看到的信息,比如版本号、负责人、代码仓库地址、环境标识等。

3.3 /actuator/metrics:指标数据的入口

metrics端点我是用得最多的,没有之一。先访问/actuator/metrics,会返回一个当前应用的指标名称列表,看起来像这样:

{ "names": [ "http.server.requests", "jvm.gc.pause", "jvm.memory.used", "jvm.threads.live", "process.uptime", "system.cpu.usage", ... ] }

想看具体指标,访问/actuator/metrics/jvm.memory.used,会返回详细测量值和可用标签:

{ "name": "jvm.memory.used", "description": "The amount of used memory", "baseUnit": "bytes", "measurements": [ { "statistic": "VALUE", "value": 104857600 } ], "availableTags": [ { "tag": "area", "values": ["heap", "nonheap"] }, { "tag": "id", "values": ["G1 Old Gen", "G1 Survivor Space", ...] } ] }

这样你就可以通过标签过滤,比如看堆内存用量:

curl "http://localhost:8080/actuator/metrics/jvm.memory.used?tag=area:heap"

这种"指标 + 标签"的设计叫多维数据模型。你可以把标签理解成给数据加的分类维度,同一份数据按不同维度切片,就能回答不同问题。排接口问题时,我会用http.server.requests配合uri、status标签去看哪些接口慢、哪些接口报错多,效果比翻日志直观得多。

3.4 /actuator/env:配置排查利器

很多"配置没生效"的问题,其实只要看一眼env端点就能定位。/actuator/env会把当前应用可见的所有环境属性列出来,包括系统环境变量、JVM 系统属性、application.yml中的配置,以及 Spring Cloud 配置中心拉下来的配置。

如果直接访问整个端点觉得信息量太大,访问单个配置项更实用:

curl http://localhost:8080/actuator/env/spring.datasource.url

返回内容会标明这个配置项来自哪个 PropertySource,比如application.yml还是系统环境变量,优先级一目了然。我在排查"为什么我改了配置文件但程序用的还是旧值"时,基本都是通过这个端点定位到真正生效的配置来源。

3.5 /actuator/loggers:运行时调整日志级别

这个端点非常实用,也非常容易用"爽"。它列出的不是日志文件内容,而是项目中所有 Logger 的当前有效级别。用 GET 请求查询单个 Logger:

curl http://localhost:8080/actuator/loggers/com.example.order

返回:

{ "configuredLevel": "INFO", "effectiveLevel": "INFO" }

要在运行时临时修改日志级别,用 POST 请求:

curl -X POST http://localhost:8080/actuator/loggers/com.example.order \ -H "Content-Type: application/json" \ -d '{"configuredLevel": "DEBUG"}'

这样做最大的价值是:不用重启应用、不用改配置、不用重新发布,就能把某个包的日志临时调到 DEBUG,排查完再改回 INFO。不过心里要有数:这种方式只对当前运行实例生效,重启后配置会被重新加载覆盖。它只该是应急调试手段,不能当配置管理工具用。

3.6 /actuator/threaddump 与 /actuator/heapdump:应急排障两板斧

系统卡顿、线程池占满、疑似死锁,优先看threaddump。访问/actuator/threaddump会得到一份完整的线程快照,包含每个线程的栈信息。你可以把它保存下来,用线程分析工具看哪些线程长期处于 BLOCKED、WAITING 状态,或者快速找到占用 CPU 最高的线程名。有一次我遇到接口迟迟不返回,就是用 threaddump 定位到某个线程卡在第三方接口读取流上,一步就锁定了问题代码。

heapdump就更重量级了。访问/actuator/heapdump会触发一次堆内存转储,返回一个二进制 hprof 文件,后期用内存分析工具打开,可以排查内存泄漏、大对象堆积、对象数量异常等问题。但注意:heapdump 对在线服务是有影响的——生成过程中可能触发较长时间的 GC 停顿,而且文件可能很大,几百 MB 到几个 GB 都常见。所以我一般只在低峰期、或者服务已经明显要不行的时候才用,不会随便对一个高并发实例执行。

3.7 /actuator/httptrace:看看最近到底来了哪些请求

httptrace端点会展示最近一段时间经过应用的 HTTP 请求明细,包括请求方法、URI、状态码、耗时、发起方 IP 等。这个端点默认情况下没有提供存储实现,在新版本里通常需要自己配置一个HttpTraceRepository的 Bean 才能看到内容,而且它只保留内存里最近一段时间的数据,适合应急看"刚才到底发生了什么"。

这里有个安全细节:httptrace如果配置不当,可能会把请求头里的敏感信息也记录下来。默认实现会做一定程度的脱敏,但你要根据自己的业务判断,是否会让会话 Token、Cookie 之类的内容出现在 trace 数据里。如果不确定,建议在自定义HttpTraceRepository时主动过滤请求头,宁可少记录也不能记录敏感信息。严格来说,httptrace更适合临时排查,生产环境长时间开启会有内存消耗和信息泄露风险。

4. 健康检查的进阶玩法:自定义 HealthIndicator 与状态聚合

4.1 默认健康检查器有哪些

很多人以为健康检查只检查数据库和 Redis,其实 Spring Boot 会根据项目引入的依赖自动注册一批健康检查器。常见的包括:

  • db:通过 DataSource 做连接有效性验证。
  • redis:检查 Redis 连接是否能正常通信。
  • rabbit、kafka、mongo、elasticsearch:对应消息中间件和数据存储。
  • diskSpace:检查磁盘剩余空间是否低于阈值。
  • ping:固定返回 UP,主要用于探活。

这些检查器都是自动装配的,平时你不需要写任何代码。但默认检查器只能覆盖框架内置组件,项目里真正依赖的那些外部服务(比如某个内部会员接口、某个核心供应商系统)它管不到,这时就需要自定义HealthIndicator。

4.2 自定义一个 HealthIndicator:判断第三方接口是否可用

实现方式很简单,只要实现HealthIndicator接口并注册为 Spring Bean 即可:

@Component public class MemberServiceHealthIndicator implements HealthIndicator { private final MemberServiceClient memberServiceClient; public MemberServiceHealthIndicator(MemberServiceClient memberServiceClient) { this.memberServiceClient = memberServiceClient; } @Override public Health health() { try { boolean up = memberServiceClient.ping(); if (up) { return Health.up() .withDetail("description", "会员服务连接正常") .withDetail("responseTimeMs", 38) .build(); } return Health.down() .withDetail("description", "会员服务 ping 失败") .build(); } catch (Exception e) { return Health.down(e) .withDetail("description", "调用会员服务发生异常") .build(); } } }

这个类一旦注册成 Bean,/actuator/health的返回里就会多出一个memberService组件。需要注意的细节:

  • Health.up()、Health.down()、Health.unknown()、Health.outOfService()这些构建器方法里,只有down和outOfService会让整体状态变成非 UP,其他健康检查器如果也有 DOWN 会直接拉低整体状态。
  • 健康检查要设置超时,不要让健康检查本身卡住。建议用@Timeout注解,或者在客户端调用里配置连接超时和读取超时,否则下游服务慢时,你的/actuator/health也会跟着变慢,监控告警反而先报你挂了。
  • 检查结果里不要放置敏感信息,健康检查结果一般会暴露给监控平台甚至负载均衡器,尽量只放状态、耗时、必要描述。

4.3 状态聚合规则与 HTTP 状态码映射

Actuator 会把所有健康检查器的状态放在一起聚合,默认状态排序逻辑是:DOWN最严重,依次是OUT_OF_SERVICE、UP、UNKNOWN。如果任何一个健康检查器返回DOWN,整体status就是DOWN;只有全部是UP时整体才是UP。

HTTP 层也有对应的状态码映射:UP时返回 200,DOWN时返回 503,OUT_OF_SERVICE时也返回 503。如果你给某个健康检查器自定义了状态,比如FATAL,需要在配置里指定它对应的 HTTP 状态码:

management: health: status: http-mapping: FATAL: 503

这套机制在负载均衡和容器编排里特别有用。阿里云 SLB、Nginx upstream、Kubernetes 探针都靠 HTTP 状态码判断服务是否健康,而不是只解析 JSON 里的 status 字段。理解了这层映射关系,你在设计健康检查时就会主动考虑外部系统的语义。

4.4 健康检查的缓存、超时与并发

健康检查本身是一把双刃剑:它收集健康信息,但也不能因为收集信息拖垮应用。默认情况下,每次请求/actuator/health都会重新执行所有健康检查器,如果这些检查器都要去请求外部系统,频繁的探活也会变成一种负担。

我的经验是给健康检查加一级缓存:

management: endpoint: health: cache: time-to-live: 10s

这样在 10 秒内,多次访问health端点会直接返回缓存结果,既保证数据相对新鲜,又避免探活请求对数据库和外部服务造成压力。如果你们有独立的监控平台每 15 秒扫一次健康端点,这个设置能明显减少无效请求。

5. 指标监控的实战姿势:从 Micrometer 到自定义业务指标

5.1 先厘清 Micrometer 和 Actuator 的关系

聊指标之前必须先捋清一个概念:Actuator 的指标端点本身不产生指标数据,真正的指标生产方是 Micrometer 指标库。Micrometer 在 Spring Boot 中类似 SLF4J 在日志体系中的角色——它是一个门面 API,定义了一套统一的指标模型,底层可以适配 Prometheus、InfluxDB、Datadog 等不同监控系统。

一个指标由名称、标签和测量值组成。名称是唯一标识,标签是维度信息,测量值就是实际数据。理解这三者的关系后,你看metrics端点的返回就不会觉得乱。比如http.server.requests这个指标,按uri和status做标签,就能拆出"每个接口的请求量"和"每个接口的错误量"。

5.2 常用的内置指标分组

Actuator 自动暴露的指标里,我实际使用频率最高的是这几组:

指标名作用常见排查场景
jvm.memory.usedJVM 堆与非堆内存用量内存增长曲线是否异常
jvm.gc.pauseGC 暂停时间与次数接口变慢时先看 GC 有没有拖后腿
jvm.threads.live活跃线程数线程池耗尽、并发异常
http.server.requestsHTTP 请求数、耗时、状态码慢接口、错误率
hikaricp.connections数据库连接池活跃/空闲连接数据库连接池耗尽
process.uptime进程运行时长进程意外重启判断

看具体指标时,多利用标签过滤。比如确认某个接口平均耗时,可以这样拼:

curl "http://localhost:8080/actuator/metrics/http.server.requests?tag=uri:/api/order/list&tag=status:200"

返回结果中会有COUNT、TOTAL_TIME、MAX等统计值,比日志里手动统计准确得多。

5.3 自定义一个业务计数器:统计订单创建量

框架自带指标再多,也覆盖不了业务语义。我经常在项目里加一些业务指标,比如统计下单成功量、退款成功量、库存扣减失败量等。用 Micrometer 做这件事非常简洁:

@Service public class OrderService { private final Counter orderCreatedCounter; public OrderService(MeterRegistry meterRegistry) { this.orderCreatedCounter = Counter.builder("order.created.count") .description("创建订单总数") .tag("department", "core-order") .register(meterRegistry); } public void createOrder(OrderCreateRequest request) { // 实际创建订单逻辑... orderCreatedCounter.increment(); } }

这里MeterRegistry是 Spring Boot 自动注入的,不需要手动创建监控系统客户端。Counter.builder("order.created.count")是定义一个递增计数器,tag("department", "core-order")是给指标打上固定的业务标签。每次调用increment()就会给这个计数加一。之后访问指标端点:

curl "http://localhost:8080/actuator/metrics/order.created.count"

就能看到当前业务订单量的计数。类似的还可以用别的方式监控瞬时活跃连接数等,大多数时候封装一小层服务即可。

5.4 接入 Prometheus 和 Grafana 的思路

Actuator 自带的metrics端点适合临时查看,但要做到持续监控和告警,还得接到专门的监控系统。最简单的一步是加 Prometheus 依赖:

<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>

加完后,Actuator 会自动暴露/actuator/prometheus端点,返回 Prometheus 格式的指标文本。Prometheus 服务端每 15 秒或者你自定义的间隔去拉取这个地址,之后在 Grafana 里配置数据源,就能做面板和告警。整套链路的核心理解点是:Micrometer 负责生成指标,Actuator 负责暴露指标,Prometheus 负责采集和存储,Grafana 负责展示和告警。各司其职,不要混在一起。

6. 端点安全与暴露控制:别把监控后台裸奔在公网

6.1 默认暴露策略里藏着哪些风险

Spring Boot 的设计初衷是"默认安全"。Web 默认只暴露health,确实降低了攻击面,但这里有个容易忽略的口子:JMX。默认情况下,JMX 会暴露几乎所有端点,包括env、loggers、metrics、threaddump等。如果你的应用部署环境没有限制 JMX 端口访问,攻击者可以通过 JMX 读取应用配置里的密码。

所以我的习惯是:除非明确需要 JMX 监控,否则在application.yml里显式关闭 JMX 暴露:

management: endpoints: jmx: exposure: include: health

或者干脆:

spring: jmx: enabled: false

生产环境我一般直接关掉 JMX,或者限定只能本机访问。少一个协议入口,就少一类安全问题。

6.2 按最小权限暴露端点的推荐配置

不同角色看不同端点,暴露面应该和职责对齐。

使用场景建议暴露的端点
外部负载均衡/容器探针health
运维基础监控health, info, metrics, prometheus
开发/测试排障额外加env, loggers, threaddump
不建议暴露shutdown、heapdump、configprops

尤其要注意shutdown端点,它默认就是禁用状态,需要加上management.endpoint.shutdown.enabled=true才会生效。我建议永远不要在生产环境启用,否则任何一个能访问到管理端口的人都能让服务优雅退出。heapdump建议也不要在公网暴露,堆转储文件里包含的类名、字符串常量、敏感数据可能远比你想象的多。

6.3 用 Spring Security 给管理端点加一道认证

如果项目本身已经引入了 Spring Security,Actuator 端点默认会被纳入 Security 过滤链,所有端点默认需要认证。但这样会造成一个问题:监控平台或负载均衡器通常没法带登录态来访问health。所以常规做法是:让一小部分端点允许匿名访问,其余端点要求管理员角色。

示例配置:

@Configuration(proxyBeanMethods = false) public class ActuatorSecurityConfig { @Bean public SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws Exception { http.securityMatcher("/actuator/**") .authorizeHttpRequests(auth -> auth .requestMatchers("/actuator/health", "/actuator/info", "/actuator/prometheus").permitAll() .anyRequest().hasRole("ACTUATOR_ADMIN") ) .httpBasic(); return http.build(); } }

这里securityMatcher("/actuator/**")只匹配管理端点的路径,不会影响业务接口的 Security 规则。需要注意:自定义了SecurityFilterChain之后,原来的认证方式(表单登录、Token 等)对/actuator路径可能就不再生效了,你需要按自己的项目情况决定用哪种认证。生产环境我通常还会叠加 IP 白名单,不只依赖用户名密码。

6.4 网络层与网关层的额外防线

代码层的认证只是防线之一,网络层的隔离往往更有效。我的推荐做法是这样:

  • 给 Actuator 设置独立的管理端口,比如management.server.port=9091,业务端口还是 8080,管理端点只在独立端口上暴露。
  • 在服务器防火墙或云安全组里限制管理端口只允许跳板机、监控机器、运维网段的 IP 访问。
  • 如果前面有 Nginx 或网关,对/actuator开头的请求统一做访问控制或者直接不转发。

这样即使代码里有疏漏,外层网络也把入口挡住了,属于多一层保险。很多团队出事不是因为框架漏洞,而是把带敏感数据的端点直接裸奔到了公网。

7. 生产环境几个坑与调优记录

7.1 健康检查拖垮数据库连接池

有一段时间我负责的服务连的数据库偶发变慢,结果健康检查也跟着异常,监控平台连环告警。后来发现根因是:每次/actuator/health请求都会触发 DataSource 连接有效性校验,频繁调用时连接池忙不过来,反而让数据库连接池压力更大。

解决办法有几个方向:

  • 给 health 加缓存:management.endpoint.health.cache.time-to-live=10s。
  • 给健康检查涉及的客户端配置合理的连接超时和读取超时,避免检查线程长期阻塞。
  • 如果数据库连接池本身比较紧张,可以调大maximum-pool-size,或者把数据源健康检查的验证请求优化成更轻量的 SQL。

健康检查对线上服务的影响一度让我很意外。它的初衷是判断服务状态,但如果设计不好,可能成为服务的压力源之一。这个坑要提前防。

7.2 heapdump 触发的严重 GC 停顿

在几十 GB 堆内存的大型应用上做过一次 heapdump,结果触发了一次长时间的 Full GC,接口延迟毛刺直接拉满,差点被监控平台判死。堆转储工具在生成快照时需要全局停顿做快照,堆越大停顿越明显。

从那以后,我的操作规范是:

  • 优先用threaddump和指标曲线定位问题,确有必要时再动heapdump。
  • 执行 heapdump 的时间尽量选在低峰期。
  • 通过独立管理端口控制访问,并且做好文件落盘位置的空间检查,别把磁盘写爆。
  • 如果对在线服务有影响,可以直接改用jmap -dump:live,format=b,file=...手动触发生成,这样能只保留从 GC 根可达的对象,文件相对小一点,但代价是会先做一次 GC,同样要注意时机。

7.3 loggers 端点改完级别,重启就没了

某个同事上线后用loggers端点把某个包的日志调成 DEBUG,日志量瞬间暴涨,磁盘差点被刷满。把级别调回 INFO 后,问题解决了,但下次版本发布重启后,又出现日志量暴涨。原因很简单:端点修改的日志级别只存在于当前运行实例内存中,重启后会按照配置重新初始化。

如果你确实需要持久化某个日志级别,应该在application.yml或日志配置里固定,而不是依赖loggers端点。它只适合临时排障,不适合做配置变更。还要记得:调完日志级别后,用完就改回来,不要一直留着 DEBUG 状态在内存里。

7.4 /actuator/env 差点泄露数据库密码

一次做安全自查时发现,项目把env端点暴露到了公网,任意人都能访问/actuator/env。如果配置里包含了数据库密码、Redis 密码、第三方密钥,直接就能被读到。这是 Actuator 里最隐蔽、后果最严重的问题之一。

如果你的业务确实需要env端点做排障,请一定:

  • 限制在内网或者跳板机才能访问。
  • 配合 Spring Security 加角色校验。
  • 考虑对敏感字段做脱敏处理,或者直接用配置中心替代本地明文敏感配置。

我在实测中一般默认不暴露env,它有需要的时候通过独立管理端口加白名单访问,而不是作为对外开放的端点之一。

7.5 版本升级带来的端点路径和配置项变化

Spring Boot 2.x 到 3.x 之间,Actuator 的一些行为有过调整。比如原来management.endpoints.web.base-path依然支持,但某些端点内部结构和依赖有变化;再比如httptrace端点在新版本里不再提供默认内存实现,需要自己配置。升级后如果发现端点访问报 404 或返回值少字段,先不要怀疑代码,去查一下当前版本对应的端点文档和配置项名。

做好版本升级验证的最简单方式:本地起一个新版本项目,打开include: "*",把常用端点都请求一遍,对比返回值差异,再决定要不要调整依赖或代码。

8. 一份可复制的生产基线配置与我的使用习惯

8.1 我给新项目默认使用的 Actuator 配置

经历了上面这些坑之后,我现在的做法是给每个新项目贴一套基础配置,需要再按项目实际情况调整:

management: endpoints: web: base-path: /manage exposure: include: health,info,metrics,prometheus jmx: exposure: include: health endpoint: health: probes: enabled: true cache: time-to-live: 10s server: port: 9091

这套配置里有几个关键选择:

  • base-path: /manage:把端点路径从默认的/actuator改成/manage,减少外部扫描器直接命中默认路径的概率。这不是真正的安全手段,但能挡住一部分无聊扫描。
  • exposure.include只开health,info,metrics,prometheus:业务和常规监控够用,env、loggers、heapdump这类有风险的端点不在一起开,真有需要时临时放权。
  • management.server.port=9091:独立管理端口,配合安全组只允许运维网段和监控机器访问。
  • probes.enabled=true:让 K8s 探针能直接用 liveness/readiness 子端点。

8.2 我平时排查问题时的请求顺序

配置只是基础,真正提升效率的是使用习惯。我总结一下自己日常的排查路径:

  • 发布后第一步:先看/manage/health,确认全部健康项正常,再放流量。
  • 接口变慢时:先看/manage/metrics/http.server.requests,按uri拆分找出最慢的接口,再配合/manage/metrics/jvm.gc.pause看是不是 GC 导致的毛刺。
  • 疑似连接池问题:直接看hikaricp.connections的 active 和 pending 指标,比去数据库查信息快得多。
  • 需要快速定位代码问题:用loggers端点把目标包临时调成 DEBUG,等拿到日志再改回原级别。
  • 怀疑线程卡死或死锁:拉一次threaddump,用线程分析工具看 BLOCKED 线程和老线程,路径基本能确定问题所在。

这套路径用熟了以后,大多数线上问题都可以在十分钟内完成粗定位,再决定是查代码还是查基础设施。

8.3 后续值得继续深挖的方向

Actuator 本身只是入口,这 Round 之后我计划继续做的是:把项目里的关键业务指标全面埋点,用 Micrometer 的 Timer、DistributionSummary 做耗时分布统计;然后把指标接到统一的监控大盘,配置告警规则;再往后是结合 OpenTelemetry 做链路追踪。这一层从"服务健康"走到"业务健康",才是监控体系真正完善的地方。后面如果有新的实践,我会继续整理成笔记更新。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询